chore: migrate all Coder modules to Registry repo (#4)

Addresses part of https://github.com/coder/internal/issues/532 (but doesn't fully close it out).

This is a huge PR, but chunking it up seemed pointless, since we're largely copying over existing files. I'm going to try commenting on the main areas I think are worth paying attention to

## Changes made
- Migrated over all Coder modules from coder/modules, and put them in their correct user namespaces.
- Added README.md files to all newly-created user namespaces
- Updated all image paths for every image used, to make sure they don't break (I switched the paths programmatically, and then manually verified every README to guarantee this).
- Added README.md files for each contributor who previously made a module
- Updated our `tsconfig.json` file to use modern libraries when run from the server, and made a custom `tsconfig.json` just for the `windows-rdp` module (which has more restrictive browser concerns)
- Added CI step to run all the module tests we currently have

## Notes
- There were a lot of Bash script files that weren't carried over in this PR, partly because I don't know Bash well enough to know (1) whether they're still needed, or (2) modify them to account for the new file structure. Those can be brought over later.
- We had a `lint.ts` file that provided some light validation of some of the modules. After going through it, there were so many bugs and issues with the code that I legitimately think that it barely provided a safety net at all. I got rid of it entirely, with the intention of adding the functionality that was originally intended to the current validation logic (to be handled in a separate PR).
- I changed how we set up the `.images` directory, because it felt like it would be chaos if a bunch of users try to throw all their images in one giant directory, with no guidelines on how to do it. I instead made it so that images should be scoped by namespace, which felt a lot more manageable. The `.icons` directory is still at the top level, because realistically, there are only going to be so many types of icons referenced, so it's fine for those to be shared.
- I don't think the `maintainer_github` and `contributor_github` fields make sense anymore, but those can be stripped out once we've updated the Registry site build step to use the new Registry repo
   - My gut instinct is to use the user namespace to determine the main owner of the module, and then add a `contributors` string list to indicate which other users have contributed meaningfully to it. We can then add validation to make sure that every value in that list exists as another namespace in the repo

## What still needs to be migrated (in separate PRs)
These are the main files of interest that still probably need to be copied over from the `/modules` repo:
- `new.sh`
- `terraform_validate.sh`
- `update-version.sh`

They're probably going to require enough changes that it's worth handling them in a separate PR.
This commit is contained in:
Michael Smith
2025-04-17 09:49:54 -04:00
committed by GitHub
parent 7fd409fa73
commit 376664ca04
200 changed files with 13132 additions and 1 deletions
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
@@ -0,0 +1,204 @@
#!/usr/bin/env bash
set -o pipefail
set -u
VERBOSE="${VERBOSE:-0}"
if [[ "${VERBOSE}" -ne "0" ]]; then
set -x
fi
# List of required environment variables
required_vars=(
"INSTATUS_API_KEY"
"INSTATUS_PAGE_ID"
"INSTATUS_COMPONENT_ID"
"VERCEL_API_KEY"
)
# Check if each required variable is set
for var in "${required_vars[@]}"; do
if [[ -z "${!var:-}" ]]; then
echo "Error: Environment variable '$var' is not set."
exit 1
fi
done
REGISTRY_BASE_URL="${REGISTRY_BASE_URL:-https://registry.coder.com}"
status=0
declare -a modules=()
declare -a failures=()
# Collect all module directories containing a main.tf file
for path in $(find . -maxdepth 2 -not -path '*/.*' -type f -name main.tf | cut -d '/' -f 2 | sort -u); do
modules+=("${path}")
done
echo "Checking modules: ${modules[*]}"
# Function to update the component status on Instatus
update_component_status() {
local component_status=$1
# see https://instatus.com/help/api/components
(curl -X PUT "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/components/$INSTATUS_COMPONENT_ID" \
-H "Authorization: Bearer $INSTATUS_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"status\": \"$component_status\"}")
}
# Function to create an incident
create_incident() {
local incident_name="Degraded Service"
local message="The following modules are experiencing issues:\n"
for i in "${!failures[@]}"; do
message+="$((i + 1)). ${failures[$i]}\n"
done
component_status="PARTIALOUTAGE"
if ((${#failures[@]} == ${#modules[@]})); then
component_status="MAJOROUTAGE"
fi
# see https://instatus.com/help/api/incidents
incident_id=$(curl -s -X POST "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/incidents" \
-H "Authorization: Bearer $INSTATUS_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"$incident_name\",
\"message\": \"$message\",
\"components\": [\"$INSTATUS_COMPONENT_ID\"],
\"status\": \"INVESTIGATING\",
\"notify\": true,
\"statuses\": [
{
\"id\": \"$INSTATUS_COMPONENT_ID\",
\"status\": \"PARTIALOUTAGE\"
}
]
}" | jq -r '.id')
echo "Created incident with ID: $incident_id"
}
# Function to check for existing unresolved incidents
check_existing_incident() {
# Fetch the latest incidents with status not equal to "RESOLVED"
local unresolved_incidents=$(curl -s -X GET "https://api.instatus.com/v1/$INSTATUS_PAGE_ID/incidents" \
-H "Authorization: Bearer $INSTATUS_API_KEY" \
-H "Content-Type: application/json" | jq -r '.incidents[] | select(.status != "RESOLVED") | .id')
if [[ -n "$unresolved_incidents" ]]; then
echo "Unresolved incidents found: $unresolved_incidents"
return 0 # Indicate that there are unresolved incidents
else
echo "No unresolved incidents found."
return 1 # Indicate that no unresolved incidents exist
fi
}
force_redeploy_registry() {
# These are not secret values; safe to just expose directly in script
local VERCEL_TEAM_SLUG="codercom"
local VERCEL_TEAM_ID="team_tGkWfhEGGelkkqUUm9nXq17r"
local VERCEL_APP="registry"
local latest_res
latest_res=$(
curl "https://api.vercel.com/v6/deployments?app=$VERCEL_APP&limit=1&slug=$VERCEL_TEAM_SLUG&teamId=$VERCEL_TEAM_ID&target=production&state=BUILDING,INITIALIZING,QUEUED,READY" \
--fail \
--silent \
--header "Authorization: Bearer $VERCEL_API_KEY" \
--header "Content-Type: application/json"
)
# If we have zero deployments, something is VERY wrong. Make the whole
# script exit with a non-zero status code
local latest_id
latest_id=$(echo "${latest_res}" | jq -r '.deployments[0].uid')
if [[ "${latest_id}" = "null" ]]; then
echo "Unable to pull any previous deployments for redeployment"
echo "Please redeploy the latest deployment manually in Vercel."
echo "https://vercel.com/codercom/registry/deployments"
exit 1
fi
local latest_date_ts_seconds
latest_date_ts_seconds=$(echo "${latest_res}" | jq -r '.deployments[0].createdAt/1000|floor')
local current_date_ts_seconds
current_date_ts_seconds="$(date +%s)"
local max_redeploy_interval_seconds=7200 # 2 hours
if ((current_date_ts_seconds - latest_date_ts_seconds < max_redeploy_interval_seconds)); then
echo "The registry was deployed less than 2 hours ago."
echo "Not automatically re-deploying the regitstry."
echo "A human reading this message should decide if a redeployment is necessary."
echo "Please check the Vercel dashboard for more information."
echo "https://vercel.com/codercom/registry/deployments"
exit 1
fi
local latest_deployment_state
latest_deployment_state="$(echo "${latest_res}" | jq -r '.deployments[0].state')"
if [[ "${latest_deployment_state}" != "READY" ]]; then
echo "Last deployment was not in READY state. Skipping redeployment."
echo "A human reading this message should decide if a redeployment is necessary."
echo "Please check the Vercel dashboard for more information."
echo "https://vercel.com/codercom/registry/deployments"
exit 1
fi
echo "============================================================="
echo "!!! Redeploying registry with deployment ID: ${latest_id} !!!"
echo "============================================================="
if ! curl -X POST "https://api.vercel.com/v13/deployments?forceNew=1&skipAutoDetectionConfirmation=1&slug=$VERCEL_TEAM_SLUG&teamId=$VERCEL_TEAM_ID" \
--fail \
--header "Authorization: Bearer $VERCEL_API_KEY" \
--header "Content-Type: application/json" \
--data-raw "{ \"deploymentId\": \"${latest_id}\", \"name\": \"${VERCEL_APP}\", \"target\": \"production\" }"; then
echo "DEPLOYMENT FAILED! Please check the Vercel dashboard for more information."
echo "https://vercel.com/codercom/registry/deployments"
exit 1
fi
}
# Check each module's accessibility
for module in "${modules[@]}"; do
# Trim leading/trailing whitespace from module name
module=$(echo "${module}" | xargs)
url="${REGISTRY_BASE_URL}/modules/${module}"
printf "=== Checking module %s at %s\n" "${module}" "${url}"
status_code=$(curl --output /dev/null --head --silent --fail --location "${url}" --retry 3 --write-out "%{http_code}")
if ((status_code != 200)); then
printf "==> FAIL(%s)\n" "${status_code}"
status=1
failures+=("${module}")
else
printf "==> OK(%s)\n" "${status_code}"
fi
done
# Determine overall status and update Instatus component
if ((status == 0)); then
echo "All modules are operational."
# set to
update_component_status "OPERATIONAL"
else
echo "The following modules have issues: ${failures[*]}"
# check if all modules are down
if ((${#failures[@]} == ${#modules[@]})); then
update_component_status "MAJOROUTAGE"
else
update_component_status "PARTIALOUTAGE"
fi
# Check if there is an existing incident before creating a new one
if ! check_existing_incident; then
create_incident
fi
# If a module is down, force a reployment to try getting things back online
# ASAP
# EDIT: registry.coder.com is no longer hosted on vercel
#force_redeploy_registry
fi
exit "${status}"
@@ -0,0 +1,23 @@
# Check modules health on registry.coder.com
name: check-registry-site-health
on:
schedule:
- cron: "0,15,30,45 * * * *" # Runs every 15 minutes
workflow_dispatch: # Allows manual triggering of the workflow if needed
jobs:
run-script:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run check.sh
run: |
./.github/scripts/check.sh
env:
INSTATUS_API_KEY: ${{ secrets.INSTATUS_API_KEY }}
INSTATUS_PAGE_ID: ${{ secrets.INSTATUS_PAGE_ID }}
INSTATUS_COMPONENT_ID: ${{ secrets.INSTATUS_COMPONENT_ID }}
VERCEL_API_KEY: ${{ secrets.VERCEL_API_KEY }}
+21 -1
View File
@@ -7,7 +7,7 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
validate-contributors:
validate-readme-files:
runs-on: ubuntu-latest
steps:
- name: Check out code
@@ -20,3 +20,23 @@ jobs:
run: go build ./scripts/contributors && ./contributors
- name: Remove build file artifact
run: rm ./contributors
test-terraform:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Terraform
uses: coder/coder/.github/actions/setup-tf@main
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
# We're using the latest version of Bun for now, but it might be worth
# reconsidering. They've pushed breaking changes in patch releases
# that have broken our CI.
# Our PR where issues started to pop up: https://github.com/coder/modules/pull/383
# The Bun PR that broke things: https://github.com/oven-sh/bun/pull/16067
bun-version: latest
- name: Install dependencies
run: bun install
- name: Run tests
run: bun test
+36
View File
@@ -0,0 +1,36 @@
name: deploy-registry
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
# Set id-token permission for gcloud
# Adding a comment because retriggering the build manually hung? I am the lord of devops and you will bend?
permissions:
contents: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@71f986410dfbc7added4569d411d040a91dc6935
with:
workload_identity_provider: projects/309789351055/locations/global/workloadIdentityPools/github-actions/providers/github
service_account: registry-v2-github@coder-registry-1.iam.gserviceaccount.com
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a
# For the time being, let's have the first couple merges to main in modules deploy a new version
# to *dev*. Once we review and make sure everything's working, we can deploy a new version to *main*.
# Maybe in the future we could automate this based on the result of E2E tests.
- name: Deploy to dev.registry.coder.com
run: |
gcloud builds triggers run 29818181-126d-4f8a-a937-f228b27d3d34 --branch dev
+8
View File
@@ -137,3 +137,11 @@ dist
# Script output
/contributors
# Terraform files generated during testing
.terraform*
*.tfstate
*.tfstate.lock.info
# Generated credentials from google-github-actions/auth
gha-creds-*.json
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 304 182" style="enable-background:new 0 0 304 182;" xml:space="preserve">
<style type="text/css">
.st0{fill:#fff;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FF9900;}
</style>
<g>
<path class="st0" d="M86.4,66.4c0,3.7,0.4,6.7,1.1,8.9c0.8,2.2,1.8,4.6,3.2,7.2c0.5,0.8,0.7,1.6,0.7,2.3c0,1-0.6,2-1.9,3l-6.3,4.2 c-0.9,0.6-1.8,0.9-2.6,0.9c-1,0-2-0.5-3-1.4C76.2,90,75,88.4,74,86.8c-1-1.7-2-3.6-3.1-5.9c-7.8,9.2-17.6,13.8-29.4,13.8 c-8.4,0-15.1-2.4-20-7.2c-4.9-4.8-7.4-11.2-7.4-19.2c0-8.5,3-15.4,9.1-20.6c6.1-5.2,14.2-7.8,24.5-7.8c3.4,0,6.9,0.3,10.6,0.8 c3.7,0.5,7.5,1.3,11.5,2.2v-7.3c0-7.6-1.6-12.9-4.7-16c-3.2-3.1-8.6-4.6-16.3-4.6c-3.5,0-7.1,0.4-10.8,1.3c-3.7,0.9-7.3,2-10.8,3.4 c-1.6,0.7-2.8,1.1-3.5,1.3c-0.7,0.2-1.2,0.3-1.6,0.3c-1.4,0-2.1-1-2.1-3.1v-4.9c0-1.6,0.2-2.8,0.7-3.5c0.5-0.7,1.4-1.4,2.8-2.1 c3.5-1.8,7.7-3.3,12.6-4.5c4.9-1.3,10.1-1.9,15.6-1.9c11.9,0,20.6,2.7,26.2,8.1c5.5,5.4,8.3,13.6,8.3,24.6V66.4z M45.8,81.6 c3.3,0,6.7-0.6,10.3-1.8c3.6-1.2,6.8-3.4,9.5-6.4c1.6-1.9,2.8-4,3.4-6.4c0.6-2.4,1-5.3,1-8.7v-4.2c-2.9-0.7-6-1.3-9.2-1.7 c-3.2-0.4-6.3-0.6-9.4-0.6c-6.7,0-11.6,1.3-14.9,4c-3.3,2.7-4.9,6.5-4.9,11.5c0,4.7,1.2,8.2,3.7,10.6 C37.7,80.4,41.2,81.6,45.8,81.6z M126.1,92.4c-1.8,0-3-0.3-3.8-1c-0.8-0.6-1.5-2-2.1-3.9L96.7,10.2c-0.6-2-0.9-3.3-0.9-4 c0-1.6,0.8-2.5,2.4-2.5h9.8c1.9,0,3.2,0.3,3.9,1c0.8,0.6,1.4,2,2,3.9l16.8,66.2l15.6-66.2c0.5-2,1.1-3.3,1.9-3.9c0.8-0.6,2.2-1,4-1 h8c1.9,0,3.2,0.3,4,1c0.8,0.6,1.5,2,1.9,3.9l15.8,67l17.3-67c0.6-2,1.3-3.3,2-3.9c0.8-0.6,2.1-1,3.9-1h9.3c1.6,0,2.5,0.8,2.5,2.5 c0,0.5-0.1,1-0.2,1.6c-0.1,0.6-0.3,1.4-0.7,2.5l-24.1,77.3c-0.6,2-1.3,3.3-2.1,3.9c-0.8,0.6-2.1,1-3.8,1h-8.6c-1.9,0-3.2-0.3-4-1 c-0.8-0.7-1.5-2-1.9-4L156,23l-15.4,64.4c-0.5,2-1.1,3.3-1.9,4c-0.8,0.7-2.2,1-4,1H126.1z M254.6,95.1c-5.2,0-10.4-0.6-15.4-1.8 c-5-1.2-8.9-2.5-11.5-4c-1.6-0.9-2.7-1.9-3.1-2.8c-0.4-0.9-0.6-1.9-0.6-2.8v-5.1c0-2.1,0.8-3.1,2.3-3.1c0.6,0,1.2,0.1,1.8,0.3 c0.6,0.2,1.5,0.6,2.5,1c3.4,1.5,7.1,2.7,11,3.5c4,0.8,7.9,1.2,11.9,1.2c6.3,0,11.2-1.1,14.6-3.3c3.4-2.2,5.2-5.4,5.2-9.5 c0-2.8-0.9-5.1-2.7-7c-1.8-1.9-5.2-3.6-10.1-5.2L246,52c-7.3-2.3-12.7-5.7-16-10.2c-3.3-4.4-5-9.3-5-14.5c0-4.2,0.9-7.9,2.7-11.1 c1.8-3.2,4.2-6,7.2-8.2c3-2.3,6.4-4,10.4-5.2c4-1.2,8.2-1.7,12.6-1.7c2.2,0,4.5,0.1,6.7,0.4c2.3,0.3,4.4,0.7,6.5,1.1 c2,0.5,3.9,1,5.7,1.6c1.8,0.6,3.2,1.2,4.2,1.8c1.4,0.8,2.4,1.6,3,2.5c0.6,0.8,0.9,1.9,0.9,3.3v4.7c0,2.1-0.8,3.2-2.3,3.2 c-0.8,0-2.1-0.4-3.8-1.2c-5.7-2.6-12.1-3.9-19.2-3.9c-5.7,0-10.2,0.9-13.3,2.8c-3.1,1.9-4.7,4.8-4.7,8.9c0,2.8,1,5.2,3,7.1 c2,1.9,5.7,3.8,11,5.5l14.2,4.5c7.2,2.3,12.4,5.5,15.5,9.6c3.1,4.1,4.6,8.8,4.6,14c0,4.3-0.9,8.2-2.6,11.6 c-1.8,3.4-4.2,6.4-7.3,8.8c-3.1,2.5-6.8,4.3-11.1,5.6C264.4,94.4,259.7,95.1,254.6,95.1z"/>
<g>
<path class="st1" d="M273.5,143.7c-32.9,24.3-80.7,37.2-121.8,37.2c-57.6,0-109.5-21.3-148.7-56.7c-3.1-2.8-0.3-6.6,3.4-4.4 c42.4,24.6,94.7,39.5,148.8,39.5c36.5,0,76.6-7.6,113.5-23.2C274.2,133.6,278.9,139.7,273.5,143.7z"/>
<path class="st1" d="M287.2,128.1c-4.2-5.4-27.8-2.6-38.5-1.3c-3.2,0.4-3.7-2.4-0.8-4.5c18.8-13.2,49.7-9.4,53.3-5 c3.6,4.5-1,35.4-18.6,50.2c-2.7,2.3-5.3,1.1-4.1-1.9C282.5,155.7,291.4,133.4,287.2,128.1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" viewBox="0 0 96 96">
<defs>
<linearGradient id="e399c19f-b68f-429d-b176-18c2117ff73c" x1="-1032.172" x2="-1059.213" y1="145.312" y2="65.426" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#114a8b"/>
<stop offset="1" stop-color="#0669bc"/>
</linearGradient>
<linearGradient id="ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15" x1="-1023.725" x2="-1029.98" y1="108.083" y2="105.968" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-opacity=".3"/>
<stop offset=".071" stop-opacity=".2"/>
<stop offset=".321" stop-opacity=".1"/>
<stop offset=".623" stop-opacity=".05"/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<linearGradient id="a7fee970-a784-4bb1-af8d-63d18e5f7db9" x1="-1027.165" x2="-997.482" y1="147.642" y2="68.561" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#3ccbf4"/>
<stop offset="1" stop-color="#2892df"/>
</linearGradient>
</defs>
<path fill="url(#e399c19f-b68f-429d-b176-18c2117ff73c)" d="M33.338 6.544h26.038l-27.03 80.087a4.152 4.152 0 0 1-3.933 2.824H8.149a4.145 4.145 0 0 1-3.928-5.47L29.404 9.368a4.152 4.152 0 0 1 3.934-2.825z"/>
<path fill="#0078d4" d="M71.175 60.261h-41.29a1.911 1.911 0 0 0-1.305 3.309l26.532 24.764a4.171 4.171 0 0 0 2.846 1.121h23.38z"/>
<path fill="url(#ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15)" d="M33.338 6.544a4.118 4.118 0 0 0-3.943 2.879L4.252 83.917a4.14 4.14 0 0 0 3.908 5.538h20.787a4.443 4.443 0 0 0 3.41-2.9l5.014-14.777 17.91 16.705a4.237 4.237 0 0 0 2.666.972H81.24L71.024 60.261l-29.781.007L59.47 6.544z"/>
<path fill="url(#a7fee970-a784-4bb1-af8d-63d18e5f7db9)" d="M66.595 9.364a4.145 4.145 0 0 0-3.928-2.82H33.648a4.146 4.146 0 0 1 3.928 2.82l25.184 74.62a4.146 4.146 0 0 1-3.928 5.472h29.02a4.146 4.146 0 0 0 3.927-5.472z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg width="512" height="510" viewBox="0 0 512 510" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M115.612 0H396.387C459.974 0 512 52.026 512 115.612V394.027C512 457.614 459.974 509.639 396.387 509.639H115.612C52.026 509.639 0 457.614 0 394.027V115.612C0 52.026 52.026 0 115.612 0Z" fill="#D77655"/>
<path d="M142.27 316.619L215.925 275.293L217.163 271.704L215.925 269.708L212.336 269.707L200.026 268.948L157.942 267.81L121.444 266.294L86.083 264.398L77.186 262.503L68.846 251.508L69.705 246.024L77.187 240.994L87.904 241.929L111.587 243.546L147.124 245.998L172.906 247.515L211.099 251.483H217.163L218.023 249.032L215.95 247.515L214.332 245.998L177.556 221.076L137.746 194.738L116.894 179.572L105.621 171.889L99.934 164.685L97.483 148.964L107.72 137.691L121.47 138.626L124.983 139.562L138.911 150.278L168.66 173.305L207.508 201.917L213.195 206.644L215.47 205.027L215.748 203.889L213.195 199.618L192.065 161.425L169.519 122.577L159.484 106.476L156.83 96.821C155.895 92.853 155.213 89.517 155.213 85.447L166.865 69.624L173.31 67.551L188.855 69.624L195.402 75.311L205.057 97.403L220.703 132.183L244.968 179.474L252.071 193.502L255.862 206.494L257.278 210.462L259.727 210.461V208.186L261.724 181.545L265.414 148.838L269.003 106.754L270.242 94.9L276.105 80.694L287.757 73.011L296.856 77.359L304.338 88.075L303.302 95.001L298.853 123.916L290.133 169.21L284.446 199.541H287.759L291.551 195.75L306.893 175.378L332.675 143.151L344.049 130.362L357.319 116.233L365.836 109.509L381.936 109.508L393.79 127.125L388.483 145.324L371.902 166.353L358.152 184.172L338.436 210.712L326.127 231.943L327.265 233.637L330.197 233.359L374.733 223.88L398.795 219.533L427.509 214.605L440.501 220.671L441.917 226.838L436.811 239.451L406.101 247.034L370.083 254.238L316.447 266.927L315.79 267.407L316.548 268.342L340.712 270.617L351.049 271.173H376.35L423.464 274.687L435.773 282.826L443.154 292.785L441.916 300.368L422.959 310.023L397.38 303.957L337.678 289.752L317.204 284.646L314.374 284.645V286.339L331.435 303.021L362.701 331.254L401.853 367.651L403.85 376.65L398.82 383.752L393.513 382.994L359.112 357.111L345.842 345.46L315.789 320.158L313.793 320.157V322.811L320.719 332.947L357.293 387.922L359.188 404.781L356.535 410.266L347.056 413.577L336.642 411.682L315.234 381.628L293.142 347.784L275.323 317.453L273.15 318.691L262.635 431.952L257.706 437.74L246.332 442.088L236.854 434.884L231.824 423.232L236.854 400.205L242.92 370.153L247.848 346.267L252.297 316.593L254.951 306.735L254.774 306.078L252.601 306.356L230.231 337.066L196.21 383.043L169.291 411.858L162.846 414.411L151.673 408.622L152.71 398.285L158.953 389.085L196.21 341.693L218.68 312.322L233.188 295.361L233.087 292.91H232.228L133.274 357.161L115.656 359.436L108.073 352.333L109.009 340.681L112.598 336.89L142.347 316.416L142.246 316.518L142.27 316.619Z" fill="#FCF2EE"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

+41
View File
@@ -0,0 +1,41 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.9119 99.3171C72.4869 99.9307 74.2828 99.8914 75.8725 99.1264L96.4608 89.2197C98.6242 88.1787 100 85.9892 100 83.5872V16.4133C100 14.0113 98.6243 11.8218 96.4609 10.7808L75.8725 0.873756C73.7862 -0.130129 71.3446 0.11576 69.5135 1.44695C69.252 1.63711 69.0028 1.84943 68.769 2.08341L29.3551 38.0415L12.1872 25.0096C10.589 23.7965 8.35363 23.8959 6.86933 25.2461L1.36303 30.2549C-0.452552 31.9064 -0.454633 34.7627 1.35853 36.417L16.2471 50.0001L1.35853 63.5832C-0.454633 65.2374 -0.452552 68.0938 1.36303 69.7453L6.86933 74.7541C8.35363 76.1043 10.589 76.2037 12.1872 74.9905L29.3551 61.9587L68.769 97.9167C69.3925 98.5406 70.1246 99.0104 70.9119 99.3171ZM75.0152 27.2989L45.1091 50.0001L75.0152 72.7012V27.2989Z" fill="white"/>
</mask>
<g mask="url(#mask0)">
<path d="M96.4614 10.7962L75.8569 0.875542C73.4719 -0.272773 70.6217 0.211611 68.75 2.08333L1.29858 63.5832C-0.515693 65.2373 -0.513607 68.0937 1.30308 69.7452L6.81272 74.754C8.29793 76.1042 10.5347 76.2036 12.1338 74.9905L93.3609 13.3699C96.086 11.3026 100 13.2462 100 16.6667V16.4275C100 14.0265 98.6246 11.8378 96.4614 10.7962Z" fill="#0065A9"/>
<g filter="url(#filter0_d)">
<path d="M96.4614 89.2038L75.8569 99.1245C73.4719 100.273 70.6217 99.7884 68.75 97.9167L1.29858 36.4169C-0.515693 34.7627 -0.513607 31.9063 1.30308 30.2548L6.81272 25.246C8.29793 23.8958 10.5347 23.7964 12.1338 25.0095L93.3609 86.6301C96.086 88.6974 100 86.7538 100 83.3334V83.5726C100 85.9735 98.6246 88.1622 96.4614 89.2038Z" fill="#007ACC"/>
</g>
<g filter="url(#filter1_d)">
<path d="M75.8578 99.1263C73.4721 100.274 70.6219 99.7885 68.75 97.9166C71.0564 100.223 75 98.5895 75 95.3278V4.67213C75 1.41039 71.0564 -0.223106 68.75 2.08329C70.6219 0.211402 73.4721 -0.273666 75.8578 0.873633L96.4587 10.7807C98.6234 11.8217 100 14.0112 100 16.4132V83.5871C100 85.9891 98.6234 88.1786 96.4586 89.2196L75.8578 99.1263Z" fill="#1F9CF0"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.25">
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.8511 99.3171C72.4261 99.9306 74.2221 99.8913 75.8117 99.1264L96.4 89.2197C98.5634 88.1787 99.9392 85.9892 99.9392 83.5871V16.4133C99.9392 14.0112 98.5635 11.8217 96.4001 10.7807L75.8117 0.873695C73.7255 -0.13019 71.2838 0.115699 69.4527 1.44688C69.1912 1.63705 68.942 1.84937 68.7082 2.08335L29.2943 38.0414L12.1264 25.0096C10.5283 23.7964 8.29285 23.8959 6.80855 25.246L1.30225 30.2548C-0.513334 31.9064 -0.515415 34.7627 1.29775 36.4169L16.1863 50L1.29775 63.5832C-0.515415 65.2374 -0.513334 68.0937 1.30225 69.7452L6.80855 74.754C8.29285 76.1042 10.5283 76.2036 12.1264 74.9905L29.2943 61.9586L68.7082 97.9167C69.3317 98.5405 70.0638 99.0104 70.8511 99.3171ZM74.9544 27.2989L45.0483 50L74.9544 72.7012V27.2989Z" fill="url(#paint0_linear)"/>
</g>
</g>
<defs>
<filter id="filter0_d" x="-8.39411" y="15.8291" width="116.727" height="92.2456" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset/>
<feGaussianBlur stdDeviation="4.16667"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="overlay" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<filter id="filter1_d" x="60.4167" y="-8.07558" width="47.9167" height="116.151" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset/>
<feGaussianBlur stdDeviation="4.16667"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="overlay" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<linearGradient id="paint0_linear" x1="49.9392" y1="0.257812" x2="49.9392" y2="99.7423" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg width="66" height="48" viewBox="0 0 66 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M64.3029 20.8302C62.9894 20.8302 62.1144 20.0449 62.1144 18.4331V9.17517C62.1144 3.26504 59.7268 0 53.5592 0H50.6941V6.24078H51.5697C53.9968 6.24078 55.1508 7.60467 55.1508 10.0431V18.2264C55.1508 21.7807 56.1853 23.2273 58.4535 23.9713C56.1853 24.6739 55.1508 26.1617 55.1508 29.716C55.1508 31.7412 55.1508 33.7663 55.1508 35.7916C55.1508 37.4861 55.1508 39.1393 54.7131 40.8337C54.2754 42.4044 53.5592 43.8922 52.5644 45.1733C52.0073 45.9174 51.3707 46.5373 50.6545 47.116V47.9425H53.5193C59.687 47.9425 62.0746 44.6774 62.0746 38.7672V29.5094C62.0746 27.8562 62.9103 27.1123 64.2634 27.1123H65.8944V20.8714H64.3029V20.8302Z" fill="#D9D9D9"/>
<path d="M44.8049 9.42443H35.9712C35.7722 9.42443 35.6131 9.25912 35.6131 9.05247V8.34987C35.6131 8.14322 35.7722 7.97791 35.9712 7.97791H44.8447C45.0436 7.97791 45.2028 8.14322 45.2028 8.34987V9.05247C45.2028 9.25912 45.0038 9.42443 44.8049 9.42443Z" fill="#D9D9D9"/>
<path d="M46.3171 18.3513H39.871C39.672 18.3513 39.5128 18.1859 39.5128 17.9792V17.2767C39.5128 17.0701 39.672 16.9047 39.871 16.9047H46.3171C46.5161 16.9047 46.6752 17.0701 46.6752 17.2767V17.9792C46.6752 18.1446 46.5161 18.3513 46.3171 18.3513Z" fill="#D9D9D9"/>
<path d="M48.8636 13.8879H35.9712C35.7722 13.8879 35.6131 13.7226 35.6131 13.5159V12.8133C35.6131 12.6067 35.7722 12.4413 35.9712 12.4413H48.8237C49.0228 12.4413 49.182 12.6067 49.182 12.8133V13.5159C49.182 13.6812 49.0626 13.8879 48.8636 13.8879Z" fill="#D9D9D9"/>
<path d="M25.7449 11.4483C26.6203 11.4483 27.4958 11.531 28.3313 11.7377V10.0431C28.3313 7.64602 29.5251 6.24078 31.9126 6.24078H32.7879V0H29.923C23.7552 0 21.3679 3.26504 21.3679 9.17517V12.2336C22.7605 11.7377 24.2329 11.4483 25.7449 11.4483Z" fill="#D9D9D9"/>
<path d="M51.5695 33.9308C50.9329 28.6819 47.0333 24.3009 42.0196 23.3089C40.6269 23.0197 39.2342 22.9783 37.8813 23.2263C37.8415 23.2263 37.8415 23.1849 37.8018 23.1849C35.6132 18.4321 30.9179 15.291 25.8246 15.291C20.7313 15.291 16.0757 18.3494 13.8474 23.1023C13.8076 23.1023 13.8076 23.1437 13.7678 23.1437C12.3353 22.9783 10.9028 23.0609 9.47035 23.433C4.5362 24.6728 0.795835 28.9711 0.119377 34.1786C0.039787 34.7159 0 35.2532 0 35.7492C0 37.3196 1.03457 38.7662 2.54664 38.9729C4.41683 39.2623 6.04827 37.7743 6.00848 35.8732C6.00848 35.5838 6.00848 35.2532 6.04827 34.9639C6.36659 32.3188 8.31638 30.087 10.863 29.467C11.6589 29.2604 12.4547 29.2191 13.2107 29.3432C15.638 29.6738 18.0255 28.3925 19.06 26.1607C19.8161 24.5075 21.0098 23.0609 22.6015 22.2757C24.3522 21.4077 26.3418 21.2838 28.1723 21.9452C30.0822 22.6477 31.5146 24.1355 32.3901 25.9953C33.3053 27.814 33.743 29.0951 35.6928 29.3432C36.4886 29.467 38.7169 29.4257 39.5526 29.3844C41.184 29.3844 42.8154 29.963 43.9694 31.1616C44.7254 31.9881 45.2825 33.0214 45.5213 34.1786C45.8793 36.0385 45.4417 37.8983 44.3673 39.3035C43.6112 40.2954 42.5767 41.0394 41.4227 41.37C40.8656 41.5354 40.3085 41.5766 39.7514 41.5766C39.4332 41.5766 38.9955 41.5766 38.4782 41.5766C36.8866 41.5766 33.5043 41.5766 30.9576 41.5766C29.2069 41.5766 27.8141 40.1302 27.8141 38.3116V26.2019C27.8141 25.7061 27.4162 25.2928 26.9387 25.2928H25.7052C23.2778 25.334 21.3281 28.1446 21.3281 31.1202C21.3281 34.096 21.3281 41.99 21.3281 41.99C21.3281 45.2137 23.8349 47.8175 26.9387 47.8175C26.9387 47.8175 40.7464 47.7761 40.9452 47.7761C44.1285 47.4454 47.0731 45.751 49.0626 43.1472C51.0522 40.6261 51.9674 37.3196 51.5695 33.9308Z" fill="#D9D9D9"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.5 MiB

+1
View File
@@ -0,0 +1 @@
<svg width="82" height="80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><g transform="translate(-550 -124)"><g><g><g><g><path d="M551 124 631 124 631 204 551 204Z" fill="#ED7100" fill-rule="evenodd" fill-opacity="1"/><path d="M612.069 162.386C607.327 165.345 600.717 168.353 593.46 170.855 588.339 172.62 583.33 173.978 578.865 174.838 582.727 184.68 589.944 191.037 596.977 189.853 603.514 188.75 608.387 181.093 609.1 170.801L611.096 170.939C610.304 182.347 604.893 190.545 597.309 191.825 596.648 191.937 595.984 191.991 595.323 191.991 587.945 191.991 580.718 185.209 576.871 175.194 575.733 175.38 574.625 175.542 573.584 175.653 572.173 175.803 570.901 175.879 569.769 175.879 565.95 175.879 563.726 175.025 563.141 173.328 562.414 171.218 564.496 168.566 569.328 165.445L570.414 167.125C565.704 170.167 564.814 172.046 565.032 172.677 565.263 173.348 567.279 174.313 573.372 173.665 574.267 173.57 575.216 173.433 576.187 173.28 575.537 171.297 575.014 169.205 574.647 167.028 573.406 159.673 574.056 152.438 576.48 146.654 578.969 140.715 583.031 136.99 587.917 136.166 593.803 135.171 600.075 138.691 604.679 145.579L603.017 146.69C598.862 140.476 593.349 137.28 588.249 138.138 584.063 138.844 580.539 142.143 578.325 147.427 576.046 152.866 575.44 159.709 576.62 166.695 576.988 168.876 577.515 170.966 578.173 172.937 582.618 172.1 587.651 170.742 592.807 168.965 599.927 166.51 606.392 163.572 611.01 160.689 616.207 157.447 617.201 155.444 616.969 154.772 616.769 154.189 615.095 153.299 610.097 153.653L609.957 151.657C615.171 151.289 618.171 152.116 618.86 154.12 619.619 156.32 617.334 159.101 612.069 162.386" fill="#FFFFFF" fill-rule="evenodd" fill-opacity="1"/></g></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31 6V22C31 23.65 29.65 25 28 25H4C2.35 25 1 23.65 1 22V6C1 4.35 2.35 3 4 3H28C29.65 3 31 4.35 31 6Z" fill="#2197F3"/>
<path d="M21 27H17V24C17 23.4478 16.5522 23 16 23C15.4478 23 15 23.4478 15 24V27H11C10.4478 27 10 27.4478 10 28C10 28.5522 10.4478 29 11 29H21C21.5522 29 22 28.5522 22 28C22 27.4478 21.5522 27 21 27Z" fill="#FFC10A"/>
<path d="M31 17V22C31 23.65 29.65 25 28 25H4C2.35 25 1 23.65 1 22V17H31Z" fill="#3F51B5"/>
</svg>

After

Width:  |  Height:  |  Size: 540 B

+10
View File
@@ -0,0 +1,10 @@
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 185.69 237" enable-background="new 0 0 185.69 237" xml:space="preserve">
<g>
<path fill="#333333" d="M135.96,0h-1.06H16.93C7.58,0,0,7.58,0,16.93v203.14C0,229.42,7.58,237,16.93,237h151.83
c9.35,0,16.93-7.58,16.93-16.93V50.79v-1.06L135.96,0z"/>
<circle fill="#3FA9F5" cx="92.84" cy="118.5" r="25.39"/>
<line fill="#1A1A1A" x1="185.69" y1="50.79" x2="134.9" y2="0"/>
<path fill="#3FA9F5" d="M135.96,32.8c0,9.35,7.58,16.93,16.93,16.93h32.8L135.96,0V32.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 600 B

+1
View File
@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68.03 68.03"><defs><style>.cls-1{fill:#da291c;}</style></defs><title>Artboard 1</title><polygon class="cls-1" points="34.02 13.31 11.27 52.72 14.52 52.72 34.02 18.94 34.02 24.57 17.77 52.72 21.02 52.72 34.02 30.2 34.02 35.83 24.27 52.72 27.52 52.72 34.02 41.46 34.02 47.09 30.77 52.72 34.02 52.72 34.02 52.72 56.77 52.72 34.02 13.31"/></svg>

After

Width:  |  Height:  |  Size: 427 B

+147
View File
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xml:space="preserve"
width="560"
height="560"
version="1.1"
style="clip-rule:evenodd;fill-rule:evenodd;image-rendering:optimizeQuality;shape-rendering:geometricPrecision;text-rendering:geometricPrecision"
viewBox="0 0 560 560"
id="svg44"
sodipodi:docname="icon_raw.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
inkscape:export-filename="/home/umarcor/filebrowser/logo/icon_raw.svg.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"><metadata
id="metadata48"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="711"
id="namedview46"
showgrid="false"
inkscape:zoom="0.33714286"
inkscape:cx="-172.33051"
inkscape:cy="280"
inkscape:window-x="0"
inkscape:window-y="20"
inkscape:window-maximized="1"
inkscape:current-layer="svg44" />
<defs
id="defs4">
<style
type="text/css"
id="style2">
<![CDATA[
.fil1 {fill:#FEFEFE}
.fil6 {fill:#006498}
.fil7 {fill:#0EA5EB}
.fil8 {fill:#2979FF}
.fil3 {fill:#2BBCFF}
.fil0 {fill:#455A64}
.fil4 {fill:#53C6FC}
.fil5 {fill:#BDEAFF}
.fil2 {fill:#332C2B;fill-opacity:0.149020}
]]>
</style>
</defs>
<g
id="g85"
transform="translate(-70,-70)"><path
class="fil1"
d="M 350,71 C 504,71 629,196 629,350 629,504 504,629 350,629 196,629 71,504 71,350 71,196 196,71 350,71 Z"
id="path9"
inkscape:connector-curvature="0"
style="fill:#fefefe" /><path
class="fil2"
d="M 475,236 593,387 C 596,503 444,639 301,585 L 225,486 339,330 c 0,0 138,-95 136,-94 z"
id="path11"
inkscape:connector-curvature="0"
style="fill:#332c2b;fill-opacity:0.14902003" /><path
class="fil3"
d="m 231,211 h 208 l 38,24 v 246 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 V 219 c 0,-5 3,-8 8,-8 z"
id="path13"
inkscape:connector-curvature="0"
style="fill:#2bbcff" /><path
class="fil4"
d="m 231,211 h 208 l 38,24 v 2 L 440,214 H 231 c -4,0 -7,3 -7,7 v 263 c -1,-1 -1,-2 -1,-3 V 219 c 0,-5 3,-8 8,-8 z"
id="path15"
inkscape:connector-curvature="0"
style="fill:#53c6fc" /><polygon
class="fil5"
points="305,212 418,212 418,310 305,310 "
id="polygon17"
style="fill:#bdeaff" /><path
class="fil5"
d="m 255,363 h 189 c 3,0 5,2 5,4 V 483 H 250 V 367 c 0,-2 2,-4 5,-4 z"
id="path19"
inkscape:connector-curvature="0"
style="fill:#bdeaff" /><polygon
class="fil6"
points="250,470 449,470 449,483 250,483 "
id="polygon21"
style="fill:#006498" /><path
class="fil6"
d="m 380,226 h 10 c 3,0 6,2 6,5 v 40 c 0,3 -3,6 -6,6 h -10 c -3,0 -6,-3 -6,-6 v -40 c 0,-3 3,-5 6,-5 z"
id="path23"
inkscape:connector-curvature="0"
style="fill:#006498" /><path
class="fil1"
d="m 254,226 c 10,0 17,7 17,17 0,9 -7,16 -17,16 -9,0 -17,-7 -17,-16 0,-10 8,-17 17,-17 z"
id="path25"
inkscape:connector-curvature="0"
style="fill:#fefefe" /><path
class="fil6"
d="m 267,448 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,3 -3,3 H 267 c -2,0 -3,-2 -3,-3 v 0 c 0,-2 1,-3 3,-3 z"
id="path27"
inkscape:connector-curvature="0"
style="fill:#006498" /><path
class="fil6"
d="m 267,415 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,2 -3,2 H 267 c -2,0 -3,-1 -3,-2 v 0 c 0,-2 1,-3 3,-3 z"
id="path29"
inkscape:connector-curvature="0"
style="fill:#006498" /><path
class="fil6"
d="m 267,381 h 165 c 2,0 3,2 3,3 v 0 c 0,2 -1,3 -3,3 H 267 c -2,0 -3,-1 -3,-3 v 0 c 0,-1 1,-3 3,-3 z"
id="path31"
inkscape:connector-curvature="0"
style="fill:#006498" /><path
class="fil1"
d="m 236,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
id="path33"
inkscape:connector-curvature="0"
style="fill:#fefefe" /><path
class="fil1"
d="m 463,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
id="path35"
inkscape:connector-curvature="0"
style="fill:#fefefe" /><polygon
class="fil6"
points="305,212 284,212 284,310 305,310 "
id="polygon37"
style="fill:#006498" /><path
class="fil7"
d="m 477,479 v 2 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 v -2 c 0,4 3,8 8,8 h 238 c 5,0 8,-4 8,-8 z"
id="path39"
inkscape:connector-curvature="0"
style="fill:#0ea5eb" /><path
class="fil8"
d="M 350,70 C 505,70 630,195 630,350 630,505 505,630 350,630 195,630 70,505 70,350 70,195 195,70 350,70 Z m 0,46 C 479,116 584,221 584,350 584,479 479,584 350,584 221,584 116,479 116,350 116,221 221,116 350,116 Z"
id="path41"
inkscape:connector-curvature="0"
style="fill:#2979ff" /></g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

+1
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

+64
View File
@@ -0,0 +1,64 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M32 62L57.975 46.98V16.964L32 2L6.02602 16.964H6.02502L32 31.984V62Z" fill="url(#paint0_linear_1725:872)"/>
<path d="M57.975 16.964L32 2L6.02602 16.964H6.02502L32 31.984L57.975 16.964Z" fill="url(#paint1_linear_1725:872)"/>
<path d="M57.975 16.964L32 2L6.02602 16.964H6.02502L32 31.984L57.975 16.964Z" fill="url(#paint2_linear_1725:872)"/>
<path d="M57.975 16.964L32 2L6.02602 16.964H6.02502L32 31.984L57.975 16.964Z" fill="url(#paint3_linear_1725:872)"/>
<path d="M6.026 46.9799V16.9639L32 31.9839L6.026 46.9799Z" fill="url(#paint4_linear_1725:872)"/>
<path d="M32 31.9844L6.026 46.9804L32 62.0004V31.9844Z" fill="url(#paint5_linear_1725:872)" style="mix-blend-mode:overlay"/>
<path d="M32 61.9999L57.975 46.9799V16.9639L32 31.9839V61.9999Z" fill="url(#paint6_linear_1725:872)"/>
<path d="M32 61.9999L57.975 46.9799V16.9639L32 31.9839V61.9999Z" fill="url(#paint7_linear_1725:872)"/>
<path d="M32 61.9999L57.975 46.9799V16.9639L32 31.9839V61.9999Z" fill="url(#paint8_linear_1725:872)"/>
<g opacity="0.8">
<path d="M25.028 27.9699L32 32V2L6 16.972L25.028 27.9699Z" fill="url(#paint9_linear_1725:872)" style="mix-blend-mode:overlay"/>
</g>
<defs>
<linearGradient id="paint0_linear_1725:872" x1="17.0683" y1="11.5629" x2="61.3962" y2="55.8908" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#FDB60D"/>
<stop offset="0.54795" stop-color="#FF318C"/>
<stop offset="0.88765" stop-color="#6B57FF"/>
</linearGradient>
<linearGradient id="paint1_linear_1725:872" x1="25.5898" y1="28.0668" x2="28.4505" y2="23.112" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="white" stop-opacity="0.6"/>
<stop offset="0.08098" stop-color="#FFC524" stop-opacity="0.4"/>
<stop offset="0.70457" stop-color="#FFC524" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear_1725:872" x1="25.4334" y1="5.59024" x2="29.6963" y2="12.9738" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#F9ED32" stop-opacity="0.6"/>
<stop offset="0.19796" stop-color="#FFC524" stop-opacity="0.4"/>
<stop offset="0.70457" stop-color="#FFC524" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear_1725:872" x1="38.9129" y1="4.99057" x2="37.0135" y2="8.28029" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#FDB60D" stop-opacity="0.6"/>
<stop offset="0.23369" stop-color="#FDB60D" stop-opacity="0.4"/>
<stop offset="0.5185" stop-color="#FF318C" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear_1725:872" x1="6.02553" y1="31.972" x2="48.2774" y2="31.972" gradientUnits="userSpaceOnUse">
<stop offset="0.01477"/>
<stop offset="0.19258"/>
<stop offset="0.57156" stop-color="#6B57FF"/>
<stop offset="0.82576" stop-color="#FF318C"/>
</linearGradient>
<linearGradient id="paint5_linear_1725:872" x1="25.2369" y1="19.192" x2="18.7738" y2="55.8462" gradientUnits="userSpaceOnUse">
<stop offset="0.046628" stop-color="white" stop-opacity="0.86"/>
<stop offset="0.766654" stop-color="#CBCAA4" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint6_linear_1725:872" x1="32.0001" y1="39.4819" x2="38.997" y2="39.4819" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#FFB2FF" stop-opacity="0.6"/>
<stop offset="0.08098" stop-color="#D828FF" stop-opacity="0.4"/>
<stop offset="0.70457" stop-color="#FF318C" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint7_linear_1725:872" x1="58.0814" y1="39.4819" x2="50.9352" y2="39.4819" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6644FF" stop-opacity="0.8"/>
<stop offset="0.09676" stop-color="#6B57FF" stop-opacity="0.4"/>
<stop offset="0.70457" stop-color="#FF318C" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint8_linear_1725:872" x1="51.8655" y1="51.3952" x2="49.8093" y2="47.8337" gradientUnits="userSpaceOnUse">
<stop offset="0.00572" stop-color="#FF318C"/>
<stop offset="0.46934" stop-color="#6B57FF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint9_linear_1725:872" x1="18.9999" y1="2.93734" x2="18.9999" y2="20.5615" gradientUnits="userSpaceOnUse">
<stop offset="0.23873" stop-color="#FF5592" stop-opacity="0.65"/>
<stop offset="0.8289" stop-color="#FF57E4" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" width="512" height="512" viewBox="0 0 512 512"><style>.st0{fill:#f8bb16}.st1{fill:#ea4334}.st2{fill:#0074bc}.st3{fill:#e2e3e4}</style><g id="Layer_1_1_"><path id="path22" class="st0" d="M322.4 140.9l-44.1-76.8H162.6c-10.3 0-19.9 5.5-24.9 14.5L44.1 241.7c-5.1 8.9-5.1 19.7 0 28.7l35.6 62.1 44-76.4 66.2-115.2h132.5z"/><path id="path26" class="st1" d="M468.2 241.7L374.6 78.6c-5.2-8.9-14.7-14.5-24.9-14.5h-71.4l44.1 76.8 66.2 115.2-66.2 115.2h88l57.8-100.9c5-8.9 5-19.8 0-28.7"/><path id="path30" class="st2" d="M410.3 371.2H189.7L123.5 256l-44 76.4 58 101c5.2 8.9 14.7 14.5 24.9 14.5h187.1c10.3 0 19.9-5.5 24.9-14.5l35.9-62.2z"/><path class="st3" d="M322.2 371.2H189.6L123.5 256l66.2-115.2h132.5L388.6 256l-66.4 115.2zM256 198.5c-31.9 0-57.6 25.7-57.6 57.6s25.7 57.6 57.6 57.6 57.6-25.7 57.6-57.6-25.8-57.6-57.6-57.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 882 B

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="97" height="97">
<path fill="#F05133" d="M92.71 44.408 52.591 4.291c-2.31-2.311-6.057-2.311-8.369 0l-8.33 8.332L46.459 23.19c2.456-.83 5.272-.273 7.229 1.685 1.969 1.97 2.521 4.81 1.67 7.275l10.186 10.185c2.465-.85 5.307-.3 7.275 1.671 2.75 2.75 2.75 7.206 0 9.958-2.752 2.751-7.208 2.751-9.961 0-2.068-2.07-2.58-5.11-1.531-7.658l-9.5-9.499v24.997c.67.332 1.303.774 1.861 1.332 2.75 2.75 2.75 7.206 0 9.959-2.75 2.749-7.209 2.749-9.957 0-2.75-2.754-2.75-7.21 0-9.959.68-.679 1.467-1.193 2.307-1.537v-25.23c-.84-.344-1.625-.853-2.307-1.537-2.083-2.082-2.584-5.14-1.516-7.698L31.798 16.715 4.288 44.222c-2.311 2.313-2.311 6.06 0 8.371l40.121 40.118c2.31 2.311 6.056 2.311 8.369 0L92.71 52.779c2.311-2.311 2.311-6.06 0-8.371z"/>
</svg>

After

Width:  |  Height:  |  Size: 802 B

+1
View File
@@ -0,0 +1 @@
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 960 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="1648" height="1648" viewBox="0 0 1648 1648" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1248 0H400C179.086 0 0 179.086 0 400V1248C0 1468.91 179.086 1648 400 1648H1248C1468.91 1648 1648 1468.91 1648 1248V400C1648 179.086 1468.91 0 1248 0Z" fill="white"/>
<path d="M1293.43 1351.06C1349.36 1338.92 1408.68 1305.87 1408.68 1305.87L1304.86 1220.35C1253.53 1178.1 1209.85 1127.35 1175.69 1070.32C1128.5 991.535 1063.29 925.049 985.455 876.335L947.439 854.198C934.413 845.144 925.332 831.032 924.039 815.111C923.211 804.845 925.671 795.668 931.41 787.592C951.204 759.692 1053.52 638.291 1072.27 622.778C1096.41 602.819 1123.32 586.217 1148.28 567.209C1151.83 564.503 1155.39 561.812 1158.9 559.067C1159.02 558.944 1159.2 558.848 1159.31 558.74C1167.32 552.416 1174.88 545.699 1180.89 537.734C1202.59 512.606 1207.86 490.391 1209.15 480.56C1206.22 471.098 1197.46 449.942 1173.05 425.537C1188.35 426.476 1206.87 438.575 1223.66 452.81C1234.93 434.795 1246.73 415.76 1258.52 396.686C1266.39 383.945 1254.71 374.414 1254.39 374.117L1254.32 374.102C1254.32 374.102 1254.32 374.048 1254.31 374.036C1254.01 373.709 1244.48 362.03 1231.76 369.902C1204.58 386.72 1177.42 403.553 1153.26 418.847C1153.26 418.847 1124.62 418.25 1090.72 447.536C1082.74 453.56 1076.02 461.117 1069.71 469.112C1069.59 469.235 1069.48 469.397 1069.38 469.52C1066.62 473.015 1063.93 476.576 1061.24 480.14C1042.22 505.115 1025.63 532.01 1005.67 556.157C990.171 574.919 868.758 677.216 840.858 697.013C832.782 702.752 823.617 705.224 813.339 704.381C797.433 703.103 783.306 694.007 774.249 680.984L752.115 642.968C703.401 565.103 636.915 499.922 558.126 452.729C501.102 418.577 450.36 374.876 408.105 323.564L322.557 219.743C322.557 219.743 289.491 279.05 277.362 334.985C294.234 355.502 338.247 406.421 389.478 445.307C334.398 419.405 293.679 399.377 261.564 382.628C256.614 419.255 258.546 474.647 263.643 517.544C298.41 532.757 357.606 556.196 417.867 568.652C369.666 579.923 316.791 581.975 275.961 581.174C283.155 607.727 293.16 634.811 306.621 662.057C312.345 674.66 318.612 686.966 325.383 699.011C346.989 704.954 431.817 717.311 476.955 707.168C432.048 723.2 356.655 750.134 356.655 750.134C414.561 822.179 478.56 880.793 478.56 880.793C575.907 828.449 598.098 821.297 671.109 773.546C552.876 869.753 522.189 909.032 489 949.4L465.873 981.854C453.855 998.714 443.427 1016.62 434.712 1035.4C405.549 1098.14 364.269 1231.85 364.269 1231.85C356.901 1255.15 373.977 1272.23 396.6 1264.18C396.6 1264.18 530.28 1222.9 593.052 1193.74C611.817 1185.01 629.751 1174.58 646.596 1162.57L679.05 1139.45C689.94 1130.49 700.764 1121.71 712.647 1111.35C712.647 1111.35 794.346 1208.14 878.328 1271.81C878.328 1271.81 905.265 1196.42 921.294 1151.51C911.136 1196.66 923.496 1281.49 929.451 1303.08C941.469 1309.85 953.802 1316.12 966.405 1321.84C993.666 1335.31 1020.73 1345.31 1047.29 1352.5C1046.5 1311.66 1048.54 1258.8 1059.81 1210.6C1072.27 1270.86 1095.69 1330.07 1110.92 1364.82C1153.81 1369.92 1209.21 1371.85 1245.84 1366.9C1229.08 1334.79 1209.06 1294.04 1183.16 1238.99C1222.04 1290.22 1272.96 1334.23 1293.48 1351.1L1293.43 1351.06Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

+3
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

+90
View File
@@ -0,0 +1,90 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:figma="http://www.figma.com/figma/ns" width="44" height="51" viewBox="0 0 44 51" version="2.0">
<title>Group.svg</title>
<desc>Created using Figma 0.90</desc>
<g id="Canvas" transform="translate(-1640 -2453)" figma:type="canvas">
<g id="Group" style="mix-blend-mode:normal;" figma:type="group">
<g id="Group" style="mix-blend-mode:normal;" figma:type="group">
<g id="Group" style="mix-blend-mode:normal;" figma:type="group">
<g id="g" style="mix-blend-mode:normal;" figma:type="group">
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path9 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path0_fill" transform="translate(1640.54 2474.36)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path10 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path1_fill" transform="translate(1645.68 2474.37)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path11 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path2_fill" transform="translate(1653.39 2474.26)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path12 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path3_fill" transform="translate(1660.43 2474.39)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path13 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path4_fill" transform="translate(1667.55 2472.54)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path14 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path5_fill" transform="translate(1672.47 2474.29)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path15 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path6_fill" transform="translate(1679.98 2474.24)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
</g>
</g>
</g>
</g>
<g id="g" style="mix-blend-mode:normal;" figma:type="group">
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path16 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path7_fill" transform="translate(1673.48 2453.69)" fill="#767677" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path17 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path8_fill" transform="translate(1643.21 2484.27)" fill="#F37726" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path18 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path9_fill" transform="translate(1643.21 2457.88)" fill="#F37726" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path19 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path10_fill" transform="translate(1643.28 2496.09)" fill="#9E9E9E" style="mix-blend-mode:normal;"/>
</g>
</g>
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
<g id="path20 fill" style="mix-blend-mode:normal;" figma:type="vector">
<use xlink:href="#path11_fill" transform="translate(1641.87 2458.43)" fill="#616262" style="mix-blend-mode:normal;"/>
</g>
</g>
</g>
</g>
</g>
</g>
<defs>
<path id="path0_fill" d="M 1.74498 5.47533C 1.74498 7.03335 1.62034 7.54082 1.29983 7.91474C 0.943119 8.23595 0.480024 8.41358 0 8.41331L 0.124642 9.3036C 0.86884 9.31366 1.59095 9.05078 2.15452 8.56466C 2.45775 8.19487 2.6834 7.76781 2.818 7.30893C 2.95261 6.85005 2.99341 6.36876 2.93798 5.89377L 2.93798 0L 1.74498 0L 1.74498 5.43972L 1.74498 5.47533Z"/>
<path id="path1_fill" d="M 5.50204 4.76309C 5.50204 5.43081 5.50204 6.02731 5.55545 6.54368L 4.496 6.54368L 4.42478 5.48423C 4.20318 5.85909 3.88627 6.16858 3.50628 6.38125C 3.12628 6.59392 2.69675 6.70219 2.26135 6.69503C 1.22861 6.69503 0 6.13415 0 3.84608L 0 0.0445149L 1.193 0.0445149L 1.193 3.6057C 1.193 4.84322 1.57583 5.67119 2.65309 5.67119C 2.87472 5.67358 3.09459 5.63168 3.29982 5.54796C 3.50505 5.46424 3.69149 5.34039 3.84822 5.18366C 4.00494 5.02694 4.1288 4.84049 4.21252 4.63527C 4.29623 4.43004 4.33813 4.21016 4.33575 3.98853L 4.33575 0L 5.52874 0L 5.52874 4.72748L 5.50204 4.76309Z"/>
<path id="path2_fill" d="M 0.0534178 2.27264C 0.0534178 1.44466 0.0534178 0.768036 0 0.153731L 1.06836 0.153731L 1.12177 1.2666C 1.3598 0.864535 1.70247 0.534594 2.11325 0.311954C 2.52404 0.0893145 2.98754 -0.0176786 3.45435 0.00238095C 5.03908 0.00238095 6.23208 1.32892 6.23208 3.30538C 6.23208 5.63796 4.7987 6.79535 3.24958 6.79535C 2.85309 6.81304 2.45874 6.7281 2.10469 6.54874C 1.75064 6.36937 1.44888 6.10166 1.22861 5.77151L 1.22861 5.77151L 1.22861 9.33269L 0.0534178 9.33269L 0.0534178 2.29935L 0.0534178 2.27264ZM 1.22861 4.00872C 1.23184 4.17026 1.24972 4.33117 1.28203 4.48948C 1.38304 4.88479 1.61299 5.23513 1.93548 5.48506C 2.25798 5.735 2.65461 5.87026 3.06262 5.86944C 4.31794 5.86944 5.05689 4.8456 5.05689 3.3588C 5.05689 2.05897 4.36246 0.946096 3.10714 0.946096C 2.61036 0.986777 2.14548 1.20726 1.79965 1.5662C 1.45382 1.92514 1.25079 2.3979 1.22861 2.89585L 1.22861 4.00872Z"/>
<path id="path3_fill" d="M 1.31764 0.0178059L 2.75102 3.85499C 2.90237 4.28233 3.06262 4.7987 3.16946 5.18153C 3.2941 4.7898 3.42764 4.29123 3.5879 3.82828L 4.88773 0.0178059L 6.14305 0.0178059L 4.36246 4.64735C 3.47216 6.87309 2.92908 8.02158 2.11 8.71601C 1.69745 9.09283 1.19448 9.35658 0.649917 9.48166L 0.356119 8.48453C 0.736886 8.35942 1.09038 8.16304 1.39777 7.90584C 1.8321 7.55188 2.17678 7.10044 2.4038 6.5882C 2.45239 6.49949 2.48551 6.40314 2.50173 6.3033C 2.49161 6.19586 2.46457 6.0907 2.42161 5.9917L 0 0L 1.29983 0L 1.31764 0.0178059Z"/>
<path id="path4_fill" d="M 2.19013 0L 2.19013 1.86962L 3.8995 1.86962L 3.8995 2.75992L 2.19013 2.75992L 2.19013 6.26769C 2.19013 7.06896 2.42161 7.53191 3.08043 7.53191C 3.31442 7.53574 3.54789 7.5088 3.77486 7.45179L 3.82828 8.34208C 3.48794 8.45999 3.12881 8.51431 2.76882 8.50234C 2.53042 8.51726 2.29161 8.48043 2.06878 8.39437C 1.84595 8.30831 1.64438 8.17506 1.47789 8.00377C 1.11525 7.51873 0.949826 6.91431 1.01494 6.31221L 1.01494 2.75102L 0 2.75102L 0 1.86072L 1.03274 1.86072L 1.03274 0.275992L 2.19013 0Z"/>
<path id="path5_fill" d="M 1.17716 3.57899C 1.153 3.88093 1.19468 4.18451 1.29933 4.46876C 1.40398 4.75301 1.5691 5.01114 1.78329 5.22532C 1.99747 5.43951 2.2556 5.60463 2.53985 5.70928C 2.8241 5.81393 3.12768 5.85561 3.42962 5.83145C 4.04033 5.84511 4.64706 5.72983 5.21021 5.49313L 5.41498 6.38343C 4.72393 6.66809 3.98085 6.80458 3.23375 6.78406C 2.79821 6.81388 2.36138 6.74914 1.95322 6.59427C 1.54505 6.43941 1.17522 6.19809 0.869071 5.88688C 0.562928 5.57566 0.327723 5.2019 0.179591 4.79125C 0.0314584 4.38059 -0.0260962 3.94276 0.0108748 3.50777C 0.0108748 1.54912 1.17716 0 3.0824 0C 5.21911 0 5.75329 1.86962 5.75329 3.06262C 5.76471 3.24644 5.76471 3.43079 5.75329 3.61461L 1.15046 3.61461L 1.17716 3.57899ZM 4.66713 2.6887C 4.70149 2.45067 4.68443 2.20805 4.61709 1.97718C 4.54976 1.74631 4.43372 1.53255 4.2768 1.35031C 4.11987 1.16808 3.92571 1.0216 3.70739 0.920744C 3.48907 0.81989 3.25166 0.767006 3.01118 0.765656C 2.52201 0.801064 2.06371 1.01788 1.72609 1.37362C 1.38847 1.72935 1.19588 2.19835 1.18607 2.6887L 4.66713 2.6887Z"/>
<path id="path6_fill" d="M 0.0534178 2.19228C 0.0534178 1.42663 0.0534178 0.767806 0 0.162404L 1.06836 0.162404L 1.06836 1.43553L 1.12177 1.43553C 1.23391 1.04259 1.4656 0.694314 1.78468 0.439049C 2.10376 0.183783 2.4944 0.034196 2.90237 0.0110538C 3.01466 -0.00368459 3.12839 -0.00368459 3.24068 0.0110538L 3.24068 1.12393C 3.10462 1.10817 2.9672 1.10817 2.83114 1.12393C 2.427 1.13958 2.04237 1.30182 1.7491 1.58035C 1.45583 1.85887 1.27398 2.23462 1.23751 2.63743C 1.20422 2.8196 1.18635 3.00425 1.1841 3.18941L 1.1841 6.65267L 0.00890297 6.65267L 0.00890297 2.20118L 0.0534178 2.19228Z"/>
<path id="path7_fill" d="M 6.03059 2.83565C 6.06715 3.43376 5.92485 4.02921 5.6218 4.54615C 5.31875 5.0631 4.86869 5.47813 4.32893 5.73839C 3.78917 5.99864 3.18416 6.09233 2.59097 6.00753C 1.99778 5.92272 1.44326 5.66326 0.998048 5.26219C 0.552837 4.86113 0.23709 4.33661 0.0910307 3.75546C -0.0550287 3.17431 -0.0247891 2.56283 0.177897 1.99893C 0.380583 1.43503 0.746541 0.944221 1.22915 0.589037C 1.71176 0.233853 2.28918 0.0303686 2.88784 0.00450543C 3.28035 -0.0170932 3.67326 0.0391144 4.04396 0.169896C 4.41467 0.300677 4.75587 0.503453 5.04794 0.766561C 5.34 1.02967 5.57718 1.34792 5.74582 1.70301C 5.91446 2.0581 6.01124 2.44303 6.03059 2.83565L 6.03059 2.83565Z"/>
<path id="path8_fill" d="M 18.6962 7.12238C 10.6836 7.12238 3.64131 4.24672 0 0C 1.41284 3.82041 3.96215 7.1163 7.30479 9.44404C 10.6474 11.7718 14.623 13.0196 18.6962 13.0196C 22.7695 13.0196 26.745 11.7718 30.0877 9.44404C 33.4303 7.1163 35.9796 3.82041 37.3925 4.0486e-13C 33.7601 4.24672 26.7445 7.12238 18.6962 7.12238Z"/>
<path id="path9_fill" d="M 18.6962 5.89725C 26.7089 5.89725 33.7512 8.77291 37.3925 13.0196C 35.9796 9.19922 33.4303 5.90333 30.0877 3.57559C 26.745 1.24785 22.7695 4.0486e-13 18.6962 0C 14.623 4.0486e-13 10.6474 1.24785 7.30479 3.57559C 3.96215 5.90333 1.41284 9.19922 0 13.0196C 3.64131 8.76401 10.648 5.89725 18.6962 5.89725Z"/>
<path id="path10_fill" d="M 7.59576 3.56656C 7.64276 4.31992 7.46442 5.07022 7.08347 5.72186C 6.70251 6.3735 6.13619 6.89698 5.45666 7.22561C 4.77713 7.55424 4.01515 7.67314 3.26781 7.56716C 2.52046 7.46117 1.82158 7.13511 1.26021 6.63051C 0.698839 6.12591 0.300394 5.46561 0.115637 4.73375C -0.0691191 4.00188 -0.0318219 3.23159 0.222777 2.52099C 0.477376 1.8104 0.93775 1.19169 1.54524 0.743685C 2.15274 0.295678 2.87985 0.0386595 3.63394 0.00537589C 4.12793 -0.0210471 4.62229 0.0501173 5.08878 0.214803C 5.55526 0.37949 5.98473 0.63447 6.35264 0.965179C 6.72055 1.29589 7.01971 1.69584 7.233 2.1422C 7.4463 2.58855 7.56957 3.07256 7.59576 3.56656L 7.59576 3.56656Z"/>
<path id="path11_fill" d="M 2.25061 4.37943C 1.81886 4.39135 1.39322 4.27535 1.02722 4.04602C 0.661224 3.81668 0.371206 3.48424 0.193641 3.09052C 0.0160762 2.69679 -0.0411078 2.25935 0.0292804 1.83321C 0.0996686 1.40707 0.294486 1.01125 0.589233 0.695542C 0.883981 0.37983 1.2655 0.158316 1.68581 0.0588577C 2.10611 -0.0406005 2.54644 -0.0135622 2.95143 0.136572C 3.35641 0.286707 3.70796 0.553234 3.96186 0.902636C 4.21577 1.25204 4.3607 1.66872 4.37842 2.10027C 4.39529 2.6838 4.18131 3.25044 3.78293 3.67715C 3.38455 4.10387 2.83392 4.35623 2.25061 4.37943Z"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 328.5 120 135" enable-background="new 0 328.5 612 135" xml:space="preserve" style="&#10;">
<polygon fill="#185F85" points="29,434.2 29,439.3 29,446.3 47.4,457.1 51.9,459.7 58.3,463.2 62.7,455.5 64.3,447.3 58.3,441.9 29,425 "/>
<path fill="#0D84BE" d="M116.5,429.8V396c0-11.1,0-22.3,0-33.4l-12.1-0.3l-2.9,3.8l-3.8,7V419L58,442.2v8.9v12.4 C77.4,452,97.1,440.9,116.5,429.8z"/>
<polygon fill="#FFFFFF" points="18.5,385.5 18.5,351.7 0,362.6 0,429.8 18.5,440.3 18.5,406.8 58.3,429.8 58.3,429.8 58.3,408.4 36.9,396 58.3,383.9 58.3,362.6 "/>
<path fill="#1A9BD7" d="M58.3,328.8c19.1,11.1,38.8,22.6,58.3,33.8l-10.8,6l-8,4.5l-39.5-22.9L29,366.7c0-7,0-14.3,0-21.3 L58.3,328.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 819 B

+1
View File
@@ -0,0 +1 @@
<svg width="2270" height="2500" viewBox="0 0 256 282" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><g fill="#8CC84B"><path d="M116.504 3.58c6.962-3.985 16.03-4.003 22.986 0 34.995 19.774 70.001 39.517 104.99 59.303 6.581 3.707 10.983 11.031 10.916 18.614v118.968c.049 7.897-4.788 15.396-11.731 19.019-34.88 19.665-69.742 39.354-104.616 59.019-7.106 4.063-16.356 3.75-23.24-.646-10.457-6.062-20.932-12.094-31.39-18.15-2.137-1.274-4.546-2.288-6.055-4.36 1.334-1.798 3.719-2.022 5.657-2.807 4.365-1.388 8.374-3.616 12.384-5.778 1.014-.694 2.252-.428 3.224.193 8.942 5.127 17.805 10.403 26.777 15.481 1.914 1.105 3.852-.362 5.488-1.274 34.228-19.345 68.498-38.617 102.72-57.968 1.268-.61 1.969-1.956 1.866-3.345.024-39.245.006-78.497.012-117.742.145-1.576-.767-3.025-2.192-3.67-34.759-19.575-69.5-39.18-104.253-58.76a3.621 3.621 0 0 0-4.094-.006C91.2 39.257 56.465 58.88 21.712 78.454c-1.42.646-2.373 2.071-2.204 3.653.006 39.245 0 78.497 0 117.748a3.329 3.329 0 0 0 1.89 3.303c9.274 5.259 18.56 10.481 27.84 15.722 5.228 2.814 11.647 4.486 17.407 2.33 5.083-1.823 8.646-7.01 8.549-12.407.048-39.016-.024-78.038.036-117.048-.127-1.732 1.516-3.163 3.2-3 4.456-.03 8.918-.06 13.374.012 1.86-.042 3.14 1.823 2.91 3.568-.018 39.263.048 78.527-.03 117.79.012 10.464-4.287 21.85-13.966 26.97-11.924 6.177-26.662 4.867-38.442-1.056-10.198-5.09-19.93-11.097-29.947-16.55C5.368 215.886.555 208.357.604 200.466V81.497c-.073-7.74 4.504-15.197 11.29-18.85C46.768 42.966 81.636 23.27 116.504 3.58z"/><path d="M146.928 85.99c15.21-.979 31.493-.58 45.18 6.913 10.597 5.742 16.472 17.793 16.659 29.566-.296 1.588-1.956 2.464-3.472 2.355-4.413-.006-8.827.06-13.24-.03-1.872.072-2.96-1.654-3.195-3.309-1.268-5.633-4.34-11.212-9.642-13.929-8.139-4.075-17.576-3.87-26.451-3.785-6.479.344-13.446.905-18.935 4.715-4.214 2.886-5.494 8.712-3.99 13.404 1.418 3.369 5.307 4.456 8.489 5.458 18.33 4.794 37.754 4.317 55.734 10.626 7.444 2.572 14.726 7.572 17.274 15.366 3.333 10.446 1.872 22.932-5.56 31.318-6.027 6.901-14.805 10.657-23.56 12.697-11.647 2.597-23.734 2.663-35.562 1.51-11.122-1.268-22.696-4.19-31.282-11.768-7.342-6.375-10.928-16.308-10.572-25.895.085-1.619 1.697-2.748 3.248-2.615 4.444-.036 8.888-.048 13.332.006 1.775-.127 3.091 1.407 3.182 3.08.82 5.367 2.837 11 7.517 14.182 9.032 5.827 20.365 5.428 30.707 5.591 8.568-.38 18.186-.495 25.178-6.158 3.689-3.23 4.782-8.634 3.785-13.283-1.08-3.925-5.186-5.754-8.712-6.95-18.095-5.724-37.736-3.647-55.656-10.12-7.275-2.571-14.31-7.432-17.105-14.906-3.9-10.578-2.113-23.662 6.098-31.765 8.006-8.06 19.563-11.164 30.551-12.275z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 47.5 47.5" style="enable-background:new 0 0 47.5 47.5;" id="svg2" xml:space="preserve"><defs id="defs6"><clipPath id="clipPath18"><path d="M 0,38 38,38 38,0 0,0 0,38 z" id="path20"/></clipPath></defs><g transform="matrix(1.25,0,0,-1.25,0,47.5)" id="g12"><g id="g14"><g clip-path="url(#clipPath18)" id="g16"><g transform="translate(21.8486,9.4102)" id="g22"><path d="m 0,0 c -0.395,-1.346 -2.46,-1.924 -4.613,-1.291 -2.153,0.632 -3.578,2.234 -3.183,3.581 0.395,1.346 2.461,1.924 4.613,1.29 C -1.029,2.949 0.396,1.347 0,0 m -2.849,24.447 c -9.941,0 -18,-6.908 -18,-15.428 0,-1.067 0.127,-2.108 0.367,-3.113 1.779,-3.06 3.01,-1.128 8.633,1.684 5.727,2.864 0,-4 -2,-8 -0.615,-1.231 -0.281,-2.272 0.56,-3.124 2.946,-1.804 6.544,-2.876 10.44,-2.876 9.942,0 18,6.907 18,15.429 0,8.52 -8.058,15.428 -18,15.428" id="path24" style="fill:#d99e82;fill-opacity:1;fill-rule:nonzero;stroke:none"/></g><g transform="translate(14,26)" id="g26"><path d="m 0,0 c 0,-1.657 -1.343,-3 -3,-3 -1.657,0 -3,1.343 -3,3 0,1.657 1.343,3 3,3 1.657,0 3,-1.343 3,-3" id="path28" style="fill:#5c913b;fill-opacity:1;fill-rule:nonzero;stroke:none"/></g><g transform="translate(24,28)" id="g30"><path d="m 0,0 c 0,-1.657 -1.344,-3 -3,-3 -1.657,0 -3,1.343 -3,3 0,1.657 1.343,3 3,3 1.656,0 3,-1.343 3,-3" id="path32" style="fill:#226699;fill-opacity:1;fill-rule:nonzero;stroke:none"/></g><g transform="translate(33,22)" id="g34"><path d="m 0,0 c 0,-1.657 -1.344,-3 -3,-3 -1.656,0 -3,1.343 -3,3 0,1.657 1.344,3 3,3 1.656,0 3,-1.343 3,-3" id="path36" style="fill:#dd2e44;fill-opacity:1;fill-rule:nonzero;stroke:none"/></g><g transform="translate(32,13)" id="g38"><path d="m 0,0 c 0,-1.656 -1.344,-3 -3,-3 -1.656,0 -3,1.344 -3,3 0,1.656 1.344,3 3,3 1.656,0 3,-1.344 3,-3" id="path40" style="fill:#ffcc4d;fill-opacity:1;fill-rule:nonzero;stroke:none"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg width="127" height="127" xmlns="http://www.w3.org/2000/svg">
<path d="M27.2 80c0 7.3-5.9 13.2-13.2 13.2C6.7 93.2.8 87.3.8 80c0-7.3 5.9-13.2 13.2-13.2h13.2V80zm6.6 0c0-7.3 5.9-13.2 13.2-13.2 7.3 0 13.2 5.9 13.2 13.2v33c0 7.3-5.9 13.2-13.2 13.2-7.3 0-13.2-5.9-13.2-13.2V80z" fill="#E01E5A"/>
<path d="M47 27c-7.3 0-13.2-5.9-13.2-13.2C33.8 6.5 39.7.6 47 .6c7.3 0 13.2 5.9 13.2 13.2V27H47zm0 6.7c7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2H13.9C6.6 60.1.7 54.2.7 46.9c0-7.3 5.9-13.2 13.2-13.2H47z" fill="#36C5F0"/>
<path d="M99.9 46.9c0-7.3 5.9-13.2 13.2-13.2 7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2H99.9V46.9zm-6.6 0c0 7.3-5.9 13.2-13.2 13.2-7.3 0-13.2-5.9-13.2-13.2V13.8C66.9 6.5 72.8.6 80.1.6c7.3 0 13.2 5.9 13.2 13.2v33.1z" fill="#2EB67D"/>
<path d="M80.1 99.8c7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2-7.3 0-13.2-5.9-13.2-13.2V99.8h13.2zm0-6.6c-7.3 0-13.2-5.9-13.2-13.2 0-7.3 5.9-13.2 13.2-13.2h33.1c7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2H80.1z" fill="#ECB22E"/>
</svg>

After

Width:  |  Height:  |  Size: 1019 B

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none"><path fill="#FFD814" d="M0 0l7.971 15.516L16 0H0zm6.732 6.16h-1.27V4.89h1.27v1.27zm0-1.906h-1.27V2.985h1.27v1.269zm1.904 3.81h-1.27v-1.27h1.27v1.27zm0-1.905h-1.27V4.89h1.27v1.27zm0-1.905h-1.27V2.985h1.27v1.269zm1.894 1.905H9.26V4.89h1.27v1.27zM9.26 4.254V2.985h1.27v1.269H9.26z"/></svg>

After

Width:  |  Height:  |  Size: 506 B

+6
View File
@@ -0,0 +1,6 @@
{
"files.exclude": {
"**/terraform.tfstate": true,
"**/.terraform": true
}
}
+1
View File
@@ -1,2 +1,3 @@
# hub
Publish Coder modules and templates for other developers to use.
Executable
BIN
View File
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
[test]
preload = ["./setup.ts"]
+72
View File
@@ -0,0 +1,72 @@
---
display_name: MODULE_NAME
description: Describe what this module does
icon: ../../../../.icons/<A_RELEVANT_ICON>.svg
maintainer_github: GITHUB_USERNAME
verified: false
tags: [helper]
---
# MODULE_NAME
<!-- Describes what this module does -->
```tf
module "MODULE_NAME" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
}
```
<!-- Add a screencast or screenshot here put them in .images directory -->
## Examples
### Example 1
Install the Dracula theme from [OpenVSX](https://open-vsx.org/):
```tf
module "MODULE_NAME" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
agent_id = coder_agent.example.id
extensions = [
"dracula-theme.theme-dracula"
]
}
```
Enter the `<author>.<name>` into the extensions array and code-server will automatically install on start.
### Example 2
Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) file:
```tf
module "MODULE_NAME" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
agent_id = coder_agent.example.id
extensions = ["dracula-theme.theme-dracula"]
settings = {
"workbench.colorTheme" = "Dracula"
}
}
```
### Example 3
Run code-server in the background, don't fetch it from GitHub:
```tf
module "MODULE_NAME" {
source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
agent_id = coder_agent.example.id
offline = true
}
```
+108
View File
@@ -0,0 +1,108 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.17"
}
}
}
locals {
# A built-in icon like "/icon/code.svg" or a full URL of icon
icon_url = "https://raw.githubusercontent.com/coder/coder/main/site/static/icon/code.svg"
# a map of all possible values
options = {
"Option 1" = {
"name" = "Option 1",
"value" = "1"
"icon" = "/emojis/1.png"
}
"Option 2" = {
"name" = "Option 2",
"value" = "2"
"icon" = "/emojis/2.png"
}
}
}
# Add required variables for your modules and remove any unneeded variables
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "log_path" {
type = string
description = "The path to log MODULE_NAME to."
default = "/tmp/MODULE_NAME.log"
}
variable "port" {
type = number
description = "The port to run MODULE_NAME on."
default = 19999
}
variable "mutable" {
type = bool
description = "Whether the parameter is mutable."
default = true
}
variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}
# Add other variables here
resource "coder_script" "MODULE_NAME" {
agent_id = var.agent_id
display_name = "MODULE_NAME"
icon = local.icon_url
script = templatefile("${path.module}/run.sh", {
LOG_PATH : var.log_path,
})
run_on_start = true
run_on_stop = false
}
resource "coder_app" "MODULE_NAME" {
agent_id = var.agent_id
slug = "MODULE_NAME"
display_name = "MODULE_NAME"
url = "http://localhost:${var.port}"
icon = local.icon_url
subdomain = false
share = "owner"
order = var.order
# Remove if the app does not have a healthcheck endpoint
healthcheck {
url = "http://localhost:${var.port}/healthz"
interval = 5
threshold = 6
}
}
data "coder_parameter" "MODULE_NAME" {
type = "list(string)"
name = "MODULE_NAME"
display_name = "MODULE_NAME"
icon = local.icon_url
mutable = var.mutable
default = local.options["Option 1"]["value"]
dynamic "option" {
for_each = local.options
content {
icon = option.value.icon
name = option.value.name
value = option.value.value
}
}
}
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
# Convert templated variables to shell variables
# shellcheck disable=SC2269
LOG_PATH=${LOG_PATH}
# shellcheck disable=SC2034
BOLD='\033[0;1m'
# shellcheck disable=SC2059
printf "$${BOLD}Installing MODULE_NAME ...\n\n"
# Add code here
# Use varibles from the templatefile function in main.tf
# e.g. LOG_PATH, PORT, etc.
printf "🥳 Installation comlete!\n\n"
printf "👷 Starting MODULE_NAME in background...\n\n"
# Start the app in here
# 1. Use & to run it in background
# 2. redirct stdout and stderr to log files
./app > "$${LOG_PATH}" 2>&1 &
printf "check logs at %s\n\n" "$${LOG_PATH}"
+27
View File
@@ -0,0 +1,27 @@
{
"name": "modules",
"scripts": {
"test": "bun test",
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf",
"fmt:ci": "bun x prettier --check **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt -check **/*.tf .sample/main.tf",
"update-version": "./update-version.sh"
},
"devDependencies": {
"@types/bun": "^1.2.9",
"bun-types": "^1.1.23",
"gray-matter": "^4.0.3",
"marked": "^12.0.2",
"prettier": "^3.3.3",
"prettier-plugin-sh": "^0.13.1",
"prettier-plugin-terraform-formatter": "^1.2.1"
},
"peerDependencies": {
"typescript": "^5.5.4"
},
"prettier": {
"plugins": [
"prettier-plugin-sh",
"prettier-plugin-terraform-formatter"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

@@ -0,0 +1,48 @@
---
display_name: Amazon DCV Windows
description: Amazon DCV Server and Web Client for Windows
icon: ../../../../.icons/dcv.svg
maintainer_github: coder
verified: true
tags: [windows, amazon, dcv, web, desktop]
---
# Amazon DCV Windows
Amazon DCV is high performance remote display protocol that provides a secure way to deliver remote desktop and application streaming from any cloud or data center to any device, over varying network conditions.
![Amazon DCV on a Windows workspace](../../.images/amazon-dcv-windows.png)
Enable DCV Server and Web Client on Windows workspaces.
```tf
module "dcv" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/amazon-dcv-windows/coder"
version = "1.0.24"
agent_id = resource.coder_agent.main.id
}
resource "coder_metadata" "dcv" {
count = data.coder_workspace.me.start_count
resource_id = aws_instance.dev.id # id of the instance resource
item {
key = "DCV client instructions"
value = "Run `coder port-forward ${data.coder_workspace.me.name} -p ${module.dcv[count.index].port}` and connect to **localhost:${module.dcv[count.index].port}${module.dcv[count.index].web_url_path}**"
}
item {
key = "username"
value = module.dcv[count.index].username
}
item {
key = "password"
value = module.dcv[count.index].password
sensitive = true
}
}
```
## License
Amazon DCV is free to use on AWS EC2 instances but requires a license for other cloud providers. Please see the instructions [here](https://docs.aws.amazon.com/dcv/latest/adminguide/setting-up-license.html#setting-up-license-ec2) for more information.
@@ -0,0 +1,170 @@
# Terraform variables
$adminPassword = "${admin_password}"
$port = "${port}"
$webURLPath = "${web_url_path}"
function Set-LocalAdminUser {
Write-Output "[INFO] Starting Set-LocalAdminUser function"
$securePassword = ConvertTo-SecureString $adminPassword -AsPlainText -Force
Write-Output "[DEBUG] Secure password created"
Get-LocalUser -Name Administrator | Set-LocalUser -Password $securePassword
Write-Output "[INFO] Administrator password set"
Get-LocalUser -Name Administrator | Enable-LocalUser
Write-Output "[INFO] User Administrator enabled successfully"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
}
function Get-VirtualDisplayDriverRequired {
Write-Output "[INFO] Starting Get-VirtualDisplayDriverRequired function"
$token = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'} -Method PUT -Uri http://169.254.169.254/latest/api/token
Write-Output "[DEBUG] Token acquired: $token"
$instanceType = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token' = $token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-type
Write-Output "[DEBUG] Instance type: $instanceType"
$OSVersion = ((Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName) -replace "[^0-9]", ''
Write-Output "[DEBUG] OS version: $OSVersion"
# Force boolean result
$result = (($OSVersion -ne "2019") -and ($OSVersion -ne "2022") -and ($OSVersion -ne "2025")) -and (($instanceType[0] -ne 'g') -and ($instanceType[0] -ne 'p'))
Write-Output "[INFO] VirtualDisplayDriverRequired result: $result"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
return [bool]$result
}
function Download-DCV {
param (
[bool]$VirtualDisplayDriverRequired
)
Write-Output "[INFO] Starting Download-DCV function"
$downloads = @(
@{
Name = "DCV Display Driver"
Required = $VirtualDisplayDriverRequired
Path = "C:\Windows\Temp\DCVDisplayDriver.msi"
Uri = "https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-virtual-display-x64-Release.msi"
},
@{
Name = "DCV Server"
Required = $true
Path = "C:\Windows\Temp\DCVServer.msi"
Uri = "https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-server-x64-Release.msi"
}
)
foreach ($download in $downloads) {
if ($download.Required -and -not (Test-Path $download.Path)) {
try {
Write-Output "[INFO] Downloading $($download.Name)"
# Display progress manually (no events)
$progressActivity = "Downloading $($download.Name)"
$progressStatus = "Starting download..."
Write-Progress -Activity $progressActivity -Status $progressStatus -PercentComplete 0
# Synchronously download the file
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($download.Uri, $download.Path)
# Update progress
Write-Progress -Activity $progressActivity -Status "Completed" -PercentComplete 100
Write-Output "[INFO] $($download.Name) downloaded successfully."
} catch {
Write-Output "[ERROR] Failed to download $($download.Name): $_"
throw
}
} else {
Write-Output "[INFO] $($download.Name) already exists. Skipping download."
}
}
Write-Output "[INFO] All downloads completed"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
}
function Install-DCV {
param (
[bool]$VirtualDisplayDriverRequired
)
Write-Output "[INFO] Starting Install-DCV function"
if (-not (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue)) {
if ($VirtualDisplayDriverRequired) {
Write-Output "[INFO] Installing DCV Display Driver"
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVDisplayDriver.msi /quiet /norestart" -Wait
} else {
Write-Output "[INFO] DCV Display Driver installation skipped (not required)."
}
Write-Output "[INFO] Installing DCV Server"
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVServer.msi ADDLOCAL=ALL /quiet /norestart /l*v C:\Windows\Temp\dcv_install_msi.log" -Wait
} else {
Write-Output "[INFO] DCV Server already installed, skipping installation."
}
# Wait for the service to appear with a timeout
$timeout = 10 # seconds
$elapsed = 0
while (-not (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue) -and ($elapsed -lt $timeout)) {
Start-Sleep -Seconds 1
$elapsed++
}
if ($elapsed -ge $timeout) {
Write-Output "[WARNING] Timeout waiting for dcvserver service. A restart is required to complete installation."
Restart-SystemForDCV
} else {
Write-Output "[INFO] dcvserver service detected successfully."
}
}
function Restart-SystemForDCV {
Write-Output "[INFO] The system will restart in 10 seconds to finalize DCV installation."
Start-Sleep -Seconds 10
# Initiate restart
Restart-Computer -Force
# Exit the script after initiating restart
Write-Output "[INFO] Please wait for the system to restart..."
Exit 1
}
function Configure-DCV {
Write-Output "[INFO] Starting Configure-DCV function"
$dcvPath = "Microsoft.PowerShell.Core\Registry::\HKEY_USERS\S-1-5-18\Software\GSettings\com\nicesoftware\dcv"
# Create the required paths
@("$dcvPath\connectivity", "$dcvPath\session-management", "$dcvPath\session-management\automatic-console-session", "$dcvPath\display") | ForEach-Object {
if (-not (Test-Path $_)) {
New-Item -Path $_ -Force | Out-Null
}
}
# Set registry keys
New-ItemProperty -Path "$dcvPath\session-management" -Name create-session -PropertyType DWORD -Value 1 -Force
New-ItemProperty -Path "$dcvPath\session-management\automatic-console-session" -Name owner -Value Administrator -Force
New-ItemProperty -Path "$dcvPath\connectivity" -Name quic-port -PropertyType DWORD -Value $port -Force
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-port -PropertyType DWORD -Value $port -Force
New-ItemProperty -Path "$dcvPath\connectivity" -Name web-url-path -PropertyType String -Value $webURLPath -Force
# Attempt to restart service
if (Get-Service -Name "dcvserver" -ErrorAction SilentlyContinue) {
Restart-Service -Name "dcvserver"
} else {
Write-Output "[WARNING] dcvserver service not found. Ensure the system was restarted properly."
}
Write-Output "[INFO] DCV configuration completed"
Read-Host "[DEBUG] Press Enter to proceed to the next step"
}
# Main Script Execution
Write-Output "[INFO] Starting script"
$VirtualDisplayDriverRequired = [bool](Get-VirtualDisplayDriverRequired)
Set-LocalAdminUser
Download-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
Install-DCV -VirtualDisplayDriverRequired $VirtualDisplayDriverRequired
Configure-DCV
Write-Output "[INFO] Script completed"
@@ -0,0 +1,85 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.17"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "admin_password" {
type = string
default = "coderDCV!"
sensitive = true
}
variable "port" {
type = number
description = "The port number for the DCV server."
default = 8443
}
variable "subdomain" {
type = bool
description = "Whether to use a subdomain for the DCV server."
default = true
}
variable "slug" {
type = string
description = "The slug of the web-dcv coder_app resource."
default = "web-dcv"
}
resource "coder_app" "web-dcv" {
agent_id = var.agent_id
slug = var.slug
display_name = "Web DCV"
url = "https://localhost:${var.port}${local.web_url_path}?username=${local.admin_username}&password=${var.admin_password}"
icon = "/icon/dcv.svg"
subdomain = var.subdomain
}
resource "coder_script" "install-dcv" {
agent_id = var.agent_id
display_name = "Install DCV"
icon = "/icon/dcv.svg"
run_on_start = true
script = templatefile("${path.module}/install-dcv.ps1", {
admin_password : var.admin_password,
port : var.port,
web_url_path : local.web_url_path
})
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
locals {
web_url_path = var.subdomain ? "/" : format("/@%s/%s/apps/%s", data.coder_workspace_owner.me.name, data.coder_workspace.me.name, var.slug)
admin_username = "Administrator"
}
output "web_url_path" {
value = local.web_url_path
}
output "username" {
value = local.admin_username
}
output "password" {
value = var.admin_password
sensitive = true
}
output "port" {
value = var.port
}
@@ -0,0 +1,24 @@
---
display_name: airflow
description: A module that adds Apache Airflow in your Coder template
icon: ../../../../.icons/airflow.svg
maintainer_github: coder
partner_github: nataindata
verified: true
tags: [airflow, idea, web, helper]
---
# airflow
A module that adds Apache Airflow in your Coder template.
```tf
module "airflow" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/apache-airflow/coder"
version = "1.0.13"
agent_id = coder_agent.main.id
}
```
![Airflow](../../.images/airflow.png)
@@ -0,0 +1,65 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.17"
}
}
}
# Add required variables for your modules and remove any unneeded variables
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "log_path" {
type = string
description = "The path to log airflow to."
default = "/tmp/airflow.log"
}
variable "port" {
type = number
description = "The port to run airflow on."
default = 8080
}
variable "share" {
type = string
default = "owner"
validation {
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
}
}
variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}
resource "coder_script" "airflow" {
agent_id = var.agent_id
display_name = "airflow"
icon = "/icon/apache-guacamole.svg"
script = templatefile("${path.module}/run.sh", {
LOG_PATH : var.log_path,
PORT : var.port
})
run_on_start = true
}
resource "coder_app" "airflow" {
agent_id = var.agent_id
slug = "airflow"
display_name = "airflow"
url = "http://localhost:${var.port}"
icon = "/icon/apache-guacamole.svg"
subdomain = true
share = var.share
order = var.order
}
@@ -0,0 +1,19 @@
#!/usr/bin/env sh
BOLD='\033[0;1m'
PATH=$PATH:~/.local/bin
pip install --upgrade apache-airflow
filename=~/airflow/airflow.db
if ! [ -f $filename ] || ! [ -s $filename ]; then
airflow db init
fi
export AIRFLOW__CORE__LOAD_EXAMPLES=false
airflow webserver > ${LOG_PATH} 2>&1 &
airflow scheduler >> /tmp/airflow_scheduler.log 2>&1 &
airflow users create -u admin -p admin -r Admin -e admin@admin.com -f Coder -l User
@@ -0,0 +1,85 @@
---
display_name: AWS Region
description: A parameter with human region names and icons
icon: ../../../../.icons/aws.svg
maintainer_github: coder
verified: true
tags: [helper, parameter, regions, aws]
---
# AWS Region
A parameter with all AWS regions. This allows developers to select
the region closest to them.
Customize the preselected parameter value:
```tf
module "aws-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/aws-region/coder"
version = "1.0.12"
default = "us-east-1"
}
provider "aws" {
region = module.aws_region.value
}
```
![AWS Regions](../../.images/aws-regions.png)
## Examples
### Customize regions
Change the display name and icon for a region using the corresponding maps:
```tf
module "aws-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/aws-region/coder"
version = "1.0.12"
default = "ap-south-1"
custom_names = {
"ap-south-1" : "Awesome Mumbai!"
}
custom_icons = {
"ap-south-1" : "/emojis/1f33a.png"
}
}
provider "aws" {
region = module.aws_region.value
}
```
![AWS Custom](../../.images/aws-custom.png)
### Exclude regions
Hide the Asia Pacific regions Seoul and Osaka:
```tf
module "aws-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/aws-region/coder"
version = "1.0.12"
exclude = ["ap-northeast-2", "ap-northeast-3"]
}
provider "aws" {
region = module.aws_region.value
}
```
![AWS Exclude](../../.images/aws-exclude.png)
## Related templates
For a complete AWS EC2 template, see the following examples in the [Coder Registry](https://registry.coder.com/).
- [AWS EC2 (Linux)](https://registry.coder.com/templates/aws-linux)
- [AWS EC2 (Windows)](https://registry.coder.com/templates/aws-windows)
@@ -0,0 +1,33 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("aws-region", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {});
it("default output", async () => {
const state = await runTerraformApply(import.meta.dir, {});
expect(state.outputs.value.value).toBe("");
});
it("customized default", async () => {
const state = await runTerraformApply(import.meta.dir, {
default: "us-west-2",
});
expect(state.outputs.value.value).toBe("us-west-2");
});
it("set custom order for coder_parameter", async () => {
const order = 99;
const state = await runTerraformApply(import.meta.dir, {
coder_parameter_order: order.toString(),
});
expect(state.resources).toHaveLength(1);
expect(state.resources[0].instances[0].attributes.order).toBe(order);
});
});
+199
View File
@@ -0,0 +1,199 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.12"
}
}
}
variable "display_name" {
default = "AWS Region"
description = "The display name of the parameter."
type = string
}
variable "description" {
default = "The region to deploy workspace infrastructure."
description = "The description of the parameter."
type = string
}
variable "default" {
default = ""
description = "The default region to use if no region is specified."
type = string
}
variable "mutable" {
default = false
description = "Whether the parameter can be changed after creation."
type = bool
}
variable "custom_names" {
default = {}
description = "A map of custom display names for region IDs."
type = map(string)
}
variable "custom_icons" {
default = {}
description = "A map of custom icons for region IDs."
type = map(string)
}
variable "exclude" {
default = []
description = "A list of region IDs to exclude."
type = list(string)
}
variable "coder_parameter_order" {
type = number
description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)."
default = null
}
locals {
# This is a static list because the regions don't change _that_
# frequently and including the `aws_regions` data source requires
# the provider, which requires a region.
regions = {
"af-south-1" = {
name = "Africa (Cape Town)"
icon = "/emojis/1f1ff-1f1e6.png"
}
"ap-east-1" = {
name = "Asia Pacific (Hong Kong)"
icon = "/emojis/1f1ed-1f1f0.png"
}
"ap-northeast-1" = {
name = "Asia Pacific (Tokyo)"
icon = "/emojis/1f1ef-1f1f5.png"
}
"ap-northeast-2" = {
name = "Asia Pacific (Seoul)"
icon = "/emojis/1f1f0-1f1f7.png"
}
"ap-northeast-3" = {
name = "Asia Pacific (Osaka)"
icon = "/emojis/1f1ef-1f1f5.png"
}
"ap-south-1" = {
name = "Asia Pacific (Mumbai)"
icon = "/emojis/1f1ee-1f1f3.png"
}
"ap-south-2" = {
name = "Asia Pacific (Hyderabad)"
icon = "/emojis/1f1ee-1f1f3.png"
}
"ap-southeast-1" = {
name = "Asia Pacific (Singapore)"
icon = "/emojis/1f1f8-1f1ec.png"
}
"ap-southeast-2" = {
name = "Asia Pacific (Sydney)"
icon = "/emojis/1f1e6-1f1fa.png"
}
"ap-southeast-3" = {
name = "Asia Pacific (Jakarta)"
icon = "/emojis/1f1ee-1f1e9.png"
}
"ap-southeast-4" = {
name = "Asia Pacific (Melbourne)"
icon = "/emojis/1f1e6-1f1fa.png"
}
"ca-central-1" = {
name = "Canada (Central)"
icon = "/emojis/1f1e8-1f1e6.png"
}
"ca-west-1" = {
name = "Canada West (Calgary)"
icon = "/emojis/1f1e8-1f1e6.png"
}
"eu-central-1" = {
name = "EU (Frankfurt)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"eu-central-2" = {
name = "Europe (Zurich)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"eu-north-1" = {
name = "EU (Stockholm)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"eu-south-1" = {
name = "Europe (Milan)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"eu-south-2" = {
name = "Europe (Spain)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"eu-west-1" = {
name = "EU (Ireland)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"eu-west-2" = {
name = "EU (London)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"eu-west-3" = {
name = "EU (Paris)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"il-central-1" = {
name = "Israel (Tel Aviv)"
icon = "/emojis/1f1ee-1f1f1.png"
}
"me-south-1" = {
name = "Middle East (Bahrain)"
icon = "/emojis/1f1e7-1f1ed.png"
}
"sa-east-1" = {
name = "South America (São Paulo)"
icon = "/emojis/1f1e7-1f1f7.png"
}
"us-east-1" = {
name = "US East (N. Virginia)"
icon = "/emojis/1f1fa-1f1f8.png"
}
"us-east-2" = {
name = "US East (Ohio)"
icon = "/emojis/1f1fa-1f1f8.png"
}
"us-west-1" = {
name = "US West (N. California)"
icon = "/emojis/1f1fa-1f1f8.png"
}
"us-west-2" = {
name = "US West (Oregon)"
icon = "/emojis/1f1fa-1f1f8.png"
}
}
}
data "coder_parameter" "region" {
name = "aws_region"
display_name = var.display_name
description = var.description
default = var.default == "" ? null : var.default
order = var.coder_parameter_order
mutable = var.mutable
dynamic "option" {
for_each = { for k, v in local.regions : k => v if !(contains(var.exclude, k)) }
content {
name = try(var.custom_names[option.key], option.value.name)
icon = try(var.custom_icons[option.key], option.value.icon)
value = option.key
}
}
}
output "value" {
value = data.coder_parameter.region.value
}
@@ -0,0 +1,84 @@
---
display_name: Azure Region
description: A parameter with human region names and icons
icon: ../../../../.icons/azure.svg
maintainer_github: coder
verified: true
tags: [helper, parameter, azure, regions]
---
# Azure Region
This module adds a parameter with all Azure regions, allowing developers to select the region closest to them.
```tf
module "azure_region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/azure-region/coder"
version = "1.0.12"
default = "eastus"
}
resource "azurem_resource_group" "example" {
location = module.azure_region.value
}
```
![Azure Region Default](../../.images/azure-default.png)
## Examples
### Customize existing regions
Change the display name and icon for a region using the corresponding maps:
```tf
module "azure-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/azure-region/coder"
version = "1.0.12"
custom_names = {
"australia" : "Go Australia!"
}
custom_icons = {
"australia" : "/icons/smiley.svg"
}
}
resource "azurerm_resource_group" "example" {
location = module.azure_region.value
}
```
![Azure Region Custom](../../.images/azure-custom.png)
### Exclude Regions
Hide all regions in Australia except australiacentral:
```tf
module "azure-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/azure-region/coder"
version = "1.0.12"
exclude = [
"australia",
"australiacentral2",
"australiaeast",
"australiasoutheast"
]
}
resource "azurerm_resource_group" "example" {
location = module.azure_region.value
}
```
![Azure Exclude](../../.images/azure-exclude.png)
## Related templates
For a complete Azure template, see the following examples in the [Coder Registry](https://registry.coder.com/).
- [Azure VM (Linux)](https://registry.coder.com/templates/azure-linux)
- [Azure VM (Windows)](https://registry.coder.com/templates/azure-windows)
@@ -0,0 +1,33 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("azure-region", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {});
it("default output", async () => {
const state = await runTerraformApply(import.meta.dir, {});
expect(state.outputs.value.value).toBe("");
});
it("customized default", async () => {
const state = await runTerraformApply(import.meta.dir, {
default: "westus",
});
expect(state.outputs.value.value).toBe("westus");
});
it("set custom order for coder_parameter", async () => {
const order = 99;
const state = await runTerraformApply(import.meta.dir, {
coder_parameter_order: order.toString(),
});
expect(state.resources).toHaveLength(1);
expect(state.resources[0].instances[0].attributes.order).toBe(order);
});
});
+333
View File
@@ -0,0 +1,333 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.11"
}
}
}
variable "display_name" {
default = "Azure Region"
description = "The display name of the Coder parameter."
type = string
}
variable "description" {
default = "The region where your workspace will live."
description = "Description of the Coder parameter."
}
variable "default" {
default = ""
description = "The default region to use if no region is specified."
type = string
}
variable "mutable" {
default = false
description = "Whether the parameter can be changed after creation."
type = bool
}
variable "custom_names" {
default = {}
description = "A map of custom display names for region IDs."
type = map(string)
}
variable "custom_icons" {
default = {}
description = "A map of custom icons for region IDs."
type = map(string)
}
variable "exclude" {
default = []
description = "A list of region IDs to exclude."
type = list(string)
}
variable "coder_parameter_order" {
type = number
description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)."
default = null
}
locals {
# Note: Options are limited to 64 regions, some redundant regions have been removed.
all_regions = {
"australia" = {
name = "Australia"
icon = "/emojis/1f1e6-1f1fa.png"
}
"australiacentral" = {
name = "Australia Central"
icon = "/emojis/1f1e6-1f1fa.png"
}
"australiacentral2" = {
name = "Australia Central 2"
icon = "/emojis/1f1e6-1f1fa.png"
}
"australiaeast" = {
name = "Australia (New South Wales)"
icon = "/emojis/1f1e6-1f1fa.png"
}
"australiasoutheast" = {
name = "Australia Southeast"
icon = "/emojis/1f1e6-1f1fa.png"
}
"brazil" = {
name = "Brazil"
icon = "/emojis/1f1e7-1f1f7.png"
}
"brazilsouth" = {
name = "Brazil (Sao Paulo)"
icon = "/emojis/1f1e7-1f1f7.png"
}
"brazilsoutheast" = {
name = "Brazil Southeast"
icon = "/emojis/1f1e7-1f1f7.png"
}
"brazilus" = {
name = "Brazil US"
icon = "/emojis/1f1e7-1f1f7.png"
}
"canada" = {
name = "Canada"
icon = "/emojis/1f1e8-1f1e6.png"
}
"canadacentral" = {
name = "Canada (Toronto)"
icon = "/emojis/1f1e8-1f1e6.png"
}
"canadaeast" = {
name = "Canada East"
icon = "/emojis/1f1e8-1f1e6.png"
}
"centralindia" = {
name = "India (Pune)"
icon = "/emojis/1f1ee-1f1f3.png"
}
"centralus" = {
name = "US (Iowa)"
icon = "/emojis/1f1fa-1f1f8.png"
}
"eastasia" = {
name = "East Asia (Hong Kong)"
icon = "/emojis/1f1f0-1f1f7.png"
}
"eastus" = {
name = "US (Virginia)"
icon = "/emojis/1f1fa-1f1f8.png"
}
"eastus2" = {
name = "US (Virginia) 2"
icon = "/emojis/1f1fa-1f1f8.png"
}
"europe" = {
name = "Europe"
icon = "/emojis/1f30d.png"
}
"france" = {
name = "France"
icon = "/emojis/1f1eb-1f1f7.png"
}
"francecentral" = {
name = "France (Paris)"
icon = "/emojis/1f1eb-1f1f7.png"
}
"francesouth" = {
name = "France South"
icon = "/emojis/1f1eb-1f1f7.png"
}
"germany" = {
name = "Germany"
icon = "/emojis/1f1e9-1f1ea.png"
}
"germanynorth" = {
name = "Germany North"
icon = "/emojis/1f1e9-1f1ea.png"
}
"germanywestcentral" = {
name = "Germany (Frankfurt)"
icon = "/emojis/1f1e9-1f1ea.png"
}
"india" = {
name = "India"
icon = "/emojis/1f1ee-1f1f3.png"
}
"japan" = {
name = "Japan"
icon = "/emojis/1f1ef-1f1f5.png"
}
"japaneast" = {
name = "Japan (Tokyo)"
icon = "/emojis/1f1ef-1f1f5.png"
}
"japanwest" = {
name = "Japan West"
icon = "/emojis/1f1ef-1f1f5.png"
}
"jioindiacentral" = {
name = "Jio India Central"
icon = "/emojis/1f1ee-1f1f3.png"
}
"jioindiawest" = {
name = "Jio India West"
icon = "/emojis/1f1ee-1f1f3.png"
}
"koreacentral" = {
name = "Korea (Seoul)"
icon = "/emojis/1f1f0-1f1f7.png"
}
"koreasouth" = {
name = "Korea South"
icon = "/emojis/1f1f0-1f1f7.png"
}
"northcentralus" = {
name = "North Central US"
icon = "/emojis/1f1fa-1f1f8.png"
}
"northeurope" = {
name = "Europe (Ireland)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"norway" = {
name = "Norway"
icon = "/emojis/1f1f3-1f1f4.png"
}
"norwayeast" = {
name = "Norway (Oslo)"
icon = "/emojis/1f1f3-1f1f4.png"
}
"norwaywest" = {
name = "Norway West"
icon = "/emojis/1f1f3-1f1f4.png"
}
"qatarcentral" = {
name = "Qatar (Doha)"
icon = "/emojis/1f1f6-1f1e6.png"
}
"singapore" = {
name = "Singapore"
icon = "/emojis/1f1f8-1f1ec.png"
}
"southafrica" = {
name = "South Africa"
icon = "/emojis/1f1ff-1f1e6.png"
}
"southafricanorth" = {
name = "South Africa (Johannesburg)"
icon = "/emojis/1f1ff-1f1e6.png"
}
"southafricawest" = {
name = "South Africa West"
icon = "/emojis/1f1ff-1f1e6.png"
}
"southcentralus" = {
name = "US (Texas)"
icon = "/emojis/1f1fa-1f1f8.png"
}
"southeastasia" = {
name = "Southeast Asia (Singapore)"
icon = "/emojis/1f1f0-1f1f7.png"
}
"southindia" = {
name = "South India"
icon = "/emojis/1f1ee-1f1f3.png"
}
"swedencentral" = {
name = "Sweden (Gävle)"
icon = "/emojis/1f1f8-1f1ea.png"
}
"switzerland" = {
name = "Switzerland"
icon = "/emojis/1f1e8-1f1ed.png"
}
"switzerlandnorth" = {
name = "Switzerland (Zurich)"
icon = "/emojis/1f1e8-1f1ed.png"
}
"switzerlandwest" = {
name = "Switzerland West"
icon = "/emojis/1f1e8-1f1ed.png"
}
"uae" = {
name = "United Arab Emirates"
icon = "/emojis/1f1e6-1f1ea.png"
}
"uaecentral" = {
name = "UAE Central"
icon = "/emojis/1f1e6-1f1ea.png"
}
"uaenorth" = {
name = "UAE (Dubai)"
icon = "/emojis/1f1e6-1f1ea.png"
}
"uk" = {
name = "United Kingdom"
icon = "/emojis/1f1ec-1f1e7.png"
}
"uksouth" = {
name = "UK (London)"
icon = "/emojis/1f1ec-1f1e7.png"
}
"ukwest" = {
name = "UK West"
icon = "/emojis/1f1ec-1f1e7.png"
}
"unitedstates" = {
name = "United States"
icon = "/emojis/1f1fa-1f1f8.png"
}
"westcentralus" = {
name = "West Central US"
icon = "/emojis/1f1fa-1f1f8.png"
}
"westeurope" = {
name = "Europe (Netherlands)"
icon = "/emojis/1f1ea-1f1fa.png"
}
"westindia" = {
name = "West India"
icon = "/emojis/1f1ee-1f1f3.png"
}
"westus" = {
name = "West US"
icon = "/emojis/1f1fa-1f1f8.png"
}
"westus2" = {
name = "US (Washington)"
icon = "/emojis/1f1fa-1f1f8.png"
}
"westus3" = {
name = "US (Arizona)"
icon = "/emojis/1f1fa-1f1f8.png"
}
}
}
data "coder_parameter" "region" {
name = "azure_region"
display_name = var.display_name
description = var.description
default = var.default == "" ? null : var.default
order = var.coder_parameter_order
mutable = var.mutable
icon = "/icon/azure.png"
dynamic "option" {
for_each = { for k, v in local.all_regions : k => v if !(contains(var.exclude, k)) }
content {
name = try(var.custom_names[option.key], option.value.name)
icon = try(var.custom_icons[option.key], option.value.icon)
value = option.key
}
}
}
output "value" {
value = data.coder_parameter.region.value
}
@@ -0,0 +1,114 @@
---
display_name: Claude Code
description: Run Claude Code in your workspace
icon: ../../../../.icons/claude.svg
maintainer_github: coder
verified: true
tags: [agent, claude-code]
---
# Claude Code
Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview) agent in your workspace to generate code and perform tasks.
```tf
module "claude-code" {
source = "registry.coder.com/modules/claude-code/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
claude_code_version = "latest"
}
```
### Prerequisites
- Node.js and npm must be installed in your workspace to install Claude Code
- `screen` must be installed in your workspace to run Claude Code in the background
- You must add the [Coder Login](https://registry.coder.com/modules/coder-login) module to your template
The `codercom/oss-dogfood:latest` container image can be used for testing on container-based workspaces.
## Examples
### Run in the background and report tasks (Experimental)
> This functionality is in early access as of Coder v2.21 and is still evolving.
> For now, we recommend testing it in a demo or staging environment,
> rather than deploying to production
>
> Learn more in [the Coder documentation](https://coder.com/docs/tutorials/ai-agents)
>
> Join our [Discord channel](https://discord.gg/coder) or
> [contact us](https://coder.com/contact) to get help or share feedback.
Your workspace must have `screen` installed to use this.
```tf
variable "anthropic_api_key" {
type = string
description = "The Anthropic API key"
sensitive = true
}
module "coder-login" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/coder-login/coder"
version = "1.0.15"
agent_id = coder_agent.example.id
}
data "coder_parameter" "ai_prompt" {
type = "string"
name = "AI Prompt"
default = ""
description = "Write a prompt for Claude Code"
mutable = true
}
# Set the prompt and system prompt for Claude Code via environment variables
resource "coder_agent" "main" {
# ...
env = {
CODER_MCP_CLAUDE_API_KEY = var.anthropic_api_key # or use a coder_parameter
CODER_MCP_CLAUDE_TASK_PROMPT = data.coder_parameter.ai_prompt.value
CODER_MCP_APP_STATUS_SLUG = "claude-code"
CODER_MCP_CLAUDE_SYSTEM_PROMPT = <<-EOT
You are a helpful assistant that can help with code.
EOT
}
}
module "claude-code" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/claude-code/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
claude_code_version = "0.2.57"
# Enable experimental features
experiment_use_screen = true
experiment_report_tasks = true
}
```
## Run standalone
Run Claude Code as a standalone app in your workspace. This will install Claude Code and run it directly without using screen or any task reporting to the Coder UI.
```tf
module "claude-code" {
source = "registry.coder.com/modules/claude-code/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
claude_code_version = "latest"
# Icon is not available in Coder v2.20 and below, so we'll use a custom icon URL
icon = "https://registry.npmmirror.com/@lobehub/icons-static-png/1.24.0/files/dark/claude-color.png"
}
```
+170
View File
@@ -0,0 +1,170 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.17"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}
variable "icon" {
type = string
description = "The icon to use for the app."
default = "/icon/claude.svg"
}
variable "folder" {
type = string
description = "The folder to run Claude Code in."
default = "/home/coder"
}
variable "install_claude_code" {
type = bool
description = "Whether to install Claude Code."
default = true
}
variable "claude_code_version" {
type = string
description = "The version of Claude Code to install."
default = "latest"
}
variable "experiment_use_screen" {
type = bool
description = "Whether to use screen for running Claude Code in the background."
default = false
}
variable "experiment_report_tasks" {
type = bool
description = "Whether to enable task reporting."
default = false
}
# Install and Initialize Claude Code
resource "coder_script" "claude_code" {
agent_id = var.agent_id
display_name = "Claude Code"
icon = var.icon
script = <<-EOT
#!/bin/bash
set -e
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Install Claude Code if enabled
if [ "${var.install_claude_code}" = "true" ]; then
if ! command_exists npm; then
echo "Error: npm is not installed. Please install Node.js and npm first."
exit 1
fi
echo "Installing Claude Code..."
npm install -g @anthropic-ai/claude-code@${var.claude_code_version}
fi
if [ "${var.experiment_report_tasks}" = "true" ]; then
echo "Configuring Claude Code to report tasks via Coder MCP..."
coder exp mcp configure claude-code ${var.folder}
fi
# Run with screen if enabled
if [ "${var.experiment_use_screen}" = "true" ]; then
echo "Running Claude Code in the background..."
# Check if screen is installed
if ! command_exists screen; then
echo "Error: screen is not installed. Please install screen manually."
exit 1
fi
touch "$HOME/.claude-code.log"
# Ensure the screenrc exists
if [ ! -f "$HOME/.screenrc" ]; then
echo "Creating ~/.screenrc and adding multiuser settings..." | tee -a "$HOME/.claude-code.log"
echo -e "multiuser on\nacladd $(whoami)" > "$HOME/.screenrc"
fi
if ! grep -q "^multiuser on$" "$HOME/.screenrc"; then
echo "Adding 'multiuser on' to ~/.screenrc..." | tee -a "$HOME/.claude-code.log"
echo "multiuser on" >> "$HOME/.screenrc"
fi
if ! grep -q "^acladd $(whoami)$" "$HOME/.screenrc"; then
echo "Adding 'acladd $(whoami)' to ~/.screenrc..." | tee -a "$HOME/.claude-code.log"
echo "acladd $(whoami)" >> "$HOME/.screenrc"
fi
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
screen -U -dmS claude-code bash -c '
cd ${var.folder}
claude --dangerously-skip-permissions | tee -a "$HOME/.claude-code.log"
exec bash
'
# Extremely hacky way to send the prompt to the screen session
# This will be fixed in the future, but `claude` was not sending MCP
# tasks when an initial prompt is provided.
screen -S claude-code -X stuff "$CODER_MCP_CLAUDE_TASK_PROMPT"
sleep 5
screen -S claude-code -X stuff "^M"
else
# Check if claude is installed before running
if ! command_exists claude; then
echo "Error: Claude Code is not installed. Please enable install_claude_code or install it manually."
exit 1
fi
fi
EOT
run_on_start = true
}
resource "coder_app" "claude_code" {
slug = "claude-code"
display_name = "Claude Code"
agent_id = var.agent_id
command = <<-EOT
#!/bin/bash
set -e
if [ "${var.experiment_use_screen}" = "true" ]; then
if screen -list | grep -q "claude-code"; then
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
echo "Attaching to existing Claude Code session." | tee -a "$HOME/.claude-code.log"
screen -xRR claude-code
else
echo "Starting a new Claude Code session." | tee -a "$HOME/.claude-code.log"
screen -S claude-code bash -c 'export LANG=en_US.UTF-8; export LC_ALL=en_US.UTF-8; claude --dangerously-skip-permissions | tee -a "$HOME/.claude-code.log"; exec bash'
fi
else
cd ${var.folder}
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
claude
fi
EOT
icon = var.icon
}
@@ -0,0 +1,115 @@
---
display_name: code-server
description: VS Code in the browser
icon: ../../../../.icons/code.svg
maintainer_github: coder
verified: true
tags: [helper, ide, web]
---
# code-server
Automatically install [code-server](https://github.com/coder/code-server) in a workspace, create an app to access it via the dashboard, install extensions, and pre-configure editor settings.
```tf
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
}
```
![Screenshot 1](https://github.com/coder/code-server/raw/main/docs/assets/screenshot-1.png?raw=true)
## Examples
### Pin Versions
```tf
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
install_version = "4.8.3"
}
```
### Pre-install Extensions
Install the Dracula theme from [OpenVSX](https://open-vsx.org/):
```tf
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
extensions = [
"dracula-theme.theme-dracula"
]
}
```
Enter the `<author>.<name>` into the extensions array and code-server will automatically install on start.
### Pre-configure Settings
Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settings-json-file) file:
```tf
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
extensions = ["dracula-theme.theme-dracula"]
settings = {
"workbench.colorTheme" = "Dracula"
}
}
```
### Install multiple extensions
Just run code-server in the background, don't fetch it from GitHub:
```tf
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"]
}
```
### Offline and Use Cached Modes
By default the module looks for code-server at `/tmp/code-server` but this can be changed with `install_prefix`.
Run an existing copy of code-server if found, otherwise download from GitHub:
```tf
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
use_cached = true
extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"]
}
```
Just run code-server in the background, don't fetch it from GitHub:
```tf
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
offline = true
}
```
@@ -0,0 +1,38 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("code-server", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
});
it("use_cached and offline can not be used together", () => {
const t = async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "foo",
use_cached: "true",
offline: "true",
});
};
expect(t).toThrow("Offline and Use Cached can not be used together");
});
it("offline and extensions can not be used together", () => {
const t = async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "foo",
offline: "true",
extensions: '["1", "2"]',
});
};
expect(t).toThrow("Offline mode does not allow extensions to be installed");
});
// More tests depend on shebang refactors
});
+175
View File
@@ -0,0 +1,175 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.17"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "extensions" {
type = list(string)
description = "A list of extensions to install."
default = []
}
variable "port" {
type = number
description = "The port to run code-server on."
default = 13337
}
variable "display_name" {
type = string
description = "The display name for the code-server application."
default = "code-server"
}
variable "slug" {
type = string
description = "The slug for the code-server application."
default = "code-server"
}
variable "settings" {
type = any
description = "A map of settings to apply to code-server."
default = {}
}
variable "folder" {
type = string
description = "The folder to open in code-server."
default = ""
}
variable "install_prefix" {
type = string
description = "The prefix to install code-server to."
default = "/tmp/code-server"
}
variable "log_path" {
type = string
description = "The path to log code-server to."
default = "/tmp/code-server.log"
}
variable "install_version" {
type = string
description = "The version of code-server to install."
default = ""
}
variable "share" {
type = string
default = "owner"
validation {
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
}
}
variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}
variable "offline" {
type = bool
description = "Just run code-server in the background, don't fetch it from GitHub"
default = false
}
variable "use_cached" {
type = bool
description = "Uses cached copy code-server in the background, otherwise fetched it from GitHub"
default = false
}
variable "use_cached_extensions" {
type = bool
description = "Uses cached copy of extensions, otherwise do a forced upgrade"
default = false
}
variable "extensions_dir" {
type = string
description = "Override the directory to store extensions in."
default = ""
}
variable "auto_install_extensions" {
type = bool
description = "Automatically install recommended extensions when code-server starts."
default = false
}
variable "subdomain" {
type = bool
description = <<-EOT
Determines whether the app will be accessed via it's own subdomain or whether it will be accessed via a path on Coder.
If wildcards have not been setup by the administrator then apps with "subdomain" set to true will not be accessible.
EOT
default = false
}
resource "coder_script" "code-server" {
agent_id = var.agent_id
display_name = "code-server"
icon = "/icon/code.svg"
script = templatefile("${path.module}/run.sh", {
VERSION : var.install_version,
EXTENSIONS : join(",", var.extensions),
APP_NAME : var.display_name,
PORT : var.port,
LOG_PATH : var.log_path,
INSTALL_PREFIX : var.install_prefix,
// This is necessary otherwise the quotes are stripped!
SETTINGS : replace(jsonencode(var.settings), "\"", "\\\""),
OFFLINE : var.offline,
USE_CACHED : var.use_cached,
USE_CACHED_EXTENSIONS : var.use_cached_extensions,
EXTENSIONS_DIR : var.extensions_dir,
FOLDER : var.folder,
AUTO_INSTALL_EXTENSIONS : var.auto_install_extensions,
})
run_on_start = true
lifecycle {
precondition {
condition = !var.offline || length(var.extensions) == 0
error_message = "Offline mode does not allow extensions to be installed"
}
precondition {
condition = !var.offline || !var.use_cached
error_message = "Offline and Use Cached can not be used together"
}
}
}
resource "coder_app" "code-server" {
agent_id = var.agent_id
slug = var.slug
display_name = var.display_name
url = "http://localhost:${var.port}/${var.folder != "" ? "?folder=${urlencode(var.folder)}" : ""}"
icon = "/icon/code.svg"
subdomain = var.subdomain
share = var.share
order = var.order
healthcheck {
url = "http://localhost:${var.port}/healthz"
interval = 5
threshold = 6
}
}
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env bash
EXTENSIONS=("${EXTENSIONS}")
BOLD='\033[0;1m'
CODE='\033[36;40;1m'
RESET='\033[0m'
CODE_SERVER="${INSTALL_PREFIX}/bin/code-server"
# Set extension directory
EXTENSION_ARG=""
if [ -n "${EXTENSIONS_DIR}" ]; then
EXTENSION_ARG="--extensions-dir=${EXTENSIONS_DIR}"
mkdir -p "${EXTENSIONS_DIR}"
fi
function run_code_server() {
echo "👷 Running code-server in the background..."
echo "Check logs at ${LOG_PATH}!"
$CODE_SERVER "$EXTENSION_ARG" --auth none --port "${PORT}" --app-name "${APP_NAME}" > "${LOG_PATH}" 2>&1 &
}
# Check if the settings file exists...
if [ ! -f ~/.local/share/code-server/User/settings.json ]; then
echo "⚙️ Creating settings file..."
mkdir -p ~/.local/share/code-server/User
echo "${SETTINGS}" > ~/.local/share/code-server/User/settings.json
fi
# Check if code-server is already installed for offline
if [ "${OFFLINE}" = true ]; then
if [ -f "$CODE_SERVER" ]; then
echo "🥳 Found a copy of code-server"
run_code_server
exit 0
fi
# Offline mode always expects a copy of code-server to be present
echo "Failed to find a copy of code-server"
exit 1
fi
# If there is no cached install OR we don't want to use a cached install
if [ ! -f "$CODE_SERVER" ] || [ "${USE_CACHED}" != true ]; then
printf "$${BOLD}Installing code-server!\n"
# Clean up from other install (in case install prefix changed).
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ -e "$CODER_SCRIPT_BIN_DIR/code-server" ]; then
rm "$CODER_SCRIPT_BIN_DIR/code-server"
fi
ARGS=(
"--method=standalone"
"--prefix=${INSTALL_PREFIX}"
)
if [ -n "${VERSION}" ]; then
ARGS+=("--version=${VERSION}")
fi
output=$(curl -fsSL https://code-server.dev/install.sh | sh -s -- "$${ARGS[@]}")
if [ $? -ne 0 ]; then
echo "Failed to install code-server: $output"
exit 1
fi
printf "🥳 code-server has been installed in ${INSTALL_PREFIX}\n\n"
fi
# Make the code-server available in PATH.
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ ! -e "$CODER_SCRIPT_BIN_DIR/code-server" ]; then
ln -s "$CODE_SERVER" "$CODER_SCRIPT_BIN_DIR/code-server"
fi
# Get the list of installed extensions...
LIST_EXTENSIONS=$($CODE_SERVER --list-extensions $EXTENSION_ARG)
readarray -t EXTENSIONS_ARRAY <<< "$LIST_EXTENSIONS"
function extension_installed() {
if [ "${USE_CACHED_EXTENSIONS}" != true ]; then
return 1
fi
for _extension in "$${EXTENSIONS_ARRAY[@]}"; do
if [ "$_extension" == "$1" ]; then
echo "Extension $1 was already installed."
return 0
fi
done
return 1
}
# Install each extension...
IFS=',' read -r -a EXTENSIONLIST <<< "$${EXTENSIONS}"
for extension in "$${EXTENSIONLIST[@]}"; do
if [ -z "$extension" ]; then
continue
fi
if extension_installed "$extension"; then
continue
fi
printf "🧩 Installing extension $${CODE}$extension$${RESET}...\n"
output=$($CODE_SERVER "$EXTENSION_ARG" --force --install-extension "$extension")
if [ $? -ne 0 ]; then
echo "Failed to install extension: $extension: $output"
exit 1
fi
done
if [ "${AUTO_INSTALL_EXTENSIONS}" = true ]; then
if ! command -v jq > /dev/null; then
echo "jq is required to install extensions from a workspace file."
exit 0
fi
WORKSPACE_DIR="$HOME"
if [ -n "${FOLDER}" ]; then
WORKSPACE_DIR="${FOLDER}"
fi
if [ -f "$WORKSPACE_DIR/.vscode/extensions.json" ]; then
printf "🧩 Installing extensions from %s/.vscode/extensions.json...\n" "$WORKSPACE_DIR"
# Use sed to remove single-line comments before parsing with jq
extensions=$(sed 's|//.*||g' "$WORKSPACE_DIR"/.vscode/extensions.json | jq -r '.recommendations[]')
for extension in $extensions; do
if extension_installed "$extension"; then
continue
fi
$CODE_SERVER "$EXTENSION_ARG" --force --install-extension "$extension"
done
fi
fi
run_code_server
@@ -0,0 +1,23 @@
---
display_name: Coder Login
description: Automatically logs the user into Coder on their workspace
icon: ../../../../.icons/coder-white.svg
maintainer_github: coder
verified: true
tags: [helper]
---
# Coder Login
Automatically logs the user into Coder when creating their workspace.
```tf
module "coder-login" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/coder-login/coder"
version = "1.0.15"
agent_id = coder_agent.example.id
}
```
![Coder Login Logs](../../.images/coder-login.png)
@@ -0,0 +1,10 @@
import { describe } from "bun:test";
import { runTerraformInit, testRequiredVariables } from "~test";
describe("coder-login", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
});
});
@@ -0,0 +1,31 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.23"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_script" "coder-login" {
agent_id = var.agent_id
script = templatefile("${path.module}/run.sh", {
CODER_USER_TOKEN : data.coder_workspace_owner.me.session_token,
CODER_DEPLOYMENT_URL : data.coder_workspace.me.access_url
})
display_name = "Coder Login"
icon = "/icon/coder.svg"
run_on_start = true
start_blocks_login = true
}
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env sh
# Automatically authenticate the user if they are not
# logged in to another deployment
BOLD='\033[0;1m'
printf "$${BOLD}Logging into Coder...\n\n$${RESET}"
if ! coder list > /dev/null 2>&1; then
set +x
coder login --token="${CODER_USER_TOKEN}" --url="${CODER_DEPLOYMENT_URL}"
else
echo "You are already authenticated with coder."
fi
+37
View File
@@ -0,0 +1,37 @@
---
display_name: Cursor IDE
description: Add a one-click button to launch Cursor IDE
icon: ../../../../.icons/cursor.svg
maintainer_github: coder
verified: true
tags: [ide, cursor, helper]
---
# Cursor IDE
Add a button to open any workspace with a single click in Cursor IDE.
Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder).
```tf
module "cursor" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/cursor/coder"
version = "1.0.19"
agent_id = coder_agent.example.id
}
```
## Examples
### Open in a specific directory
```tf
module "cursor" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/cursor/coder"
version = "1.0.19"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
}
```
@@ -0,0 +1,88 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("cursor", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
});
it("default output", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});
expect(state.outputs.cursor_url.value).toBe(
"cursor://coder.coder-remote/open?owner=default&workspace=default&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
);
const coder_app = state.resources.find(
(res) => res.type === "coder_app" && res.name === "cursor",
);
expect(coder_app).not.toBeNull();
expect(coder_app?.instances.length).toBe(1);
expect(coder_app?.instances[0].attributes.order).toBeNull();
});
it("adds folder", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
folder: "/foo/bar",
});
expect(state.outputs.cursor_url.value).toBe(
"cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
);
});
it("adds folder and open_recent", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
folder: "/foo/bar",
open_recent: "true",
});
expect(state.outputs.cursor_url.value).toBe(
"cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&openRecent&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
);
});
it("adds folder but not open_recent", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
folder: "/foo/bar",
openRecent: "false",
});
expect(state.outputs.cursor_url.value).toBe(
"cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
);
});
it("adds open_recent", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
open_recent: "true",
});
expect(state.outputs.cursor_url.value).toBe(
"cursor://coder.coder-remote/open?owner=default&workspace=default&openRecent&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
);
});
it("expect order to be set", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
order: "22",
});
const coder_app = state.resources.find(
(res) => res.type === "coder_app" && res.name === "cursor",
);
expect(coder_app).not.toBeNull();
expect(coder_app?.instances.length).toBe(1);
expect(coder_app?.instances[0].attributes.order).toBe(22);
});
});
+62
View File
@@ -0,0 +1,62 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.23"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "folder" {
type = string
description = "The folder to open in Cursor IDE."
default = ""
}
variable "open_recent" {
type = bool
description = "Open the most recent workspace or folder. Falls back to the folder if there is no recent workspace or folder to open."
default = false
}
variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_app" "cursor" {
agent_id = var.agent_id
external = true
icon = "/icon/cursor.svg"
slug = "cursor"
display_name = "Cursor Desktop"
order = var.order
url = join("", [
"cursor://coder.coder-remote/open",
"?owner=",
data.coder_workspace_owner.me.name,
"&workspace=",
data.coder_workspace.me.name,
var.folder != "" ? join("", ["&folder=", var.folder]) : "",
var.open_recent ? "&openRecent" : "",
"&url=",
data.coder_workspace.me.access_url,
"&token=$SESSION_TOKEN",
])
}
output "cursor_url" {
value = coder_app.cursor.url
description = "Cursor IDE Desktop URL."
}
+84
View File
@@ -0,0 +1,84 @@
---
display_name: Dotfiles
description: Allow developers to optionally bring their own dotfiles repository to customize their shell and IDE settings!
icon: ../../../../.icons/dotfiles.svg
maintainer_github: coder
verified: true
tags: [helper]
---
# Dotfiles
Allow developers to optionally bring their own [dotfiles repository](https://dotfiles.github.io).
This will prompt the user for their dotfiles repository URL on template creation using a `coder_parameter`.
Under the hood, this module uses the [coder dotfiles](https://coder.com/docs/v2/latest/dotfiles) command.
```tf
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
version = "1.0.29"
agent_id = coder_agent.example.id
}
```
## Examples
### Apply dotfiles as the current user
```tf
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
version = "1.0.29"
agent_id = coder_agent.example.id
}
```
### Apply dotfiles as another user (only works if sudo is passwordless)
```tf
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
version = "1.0.29"
agent_id = coder_agent.example.id
user = "root"
}
```
### Apply the same dotfiles as the current user and root (the root dotfiles can only be applied if sudo is passwordless)
```tf
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
version = "1.0.29"
agent_id = coder_agent.example.id
}
module "dotfiles-root" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
version = "1.0.29"
agent_id = coder_agent.example.id
user = "root"
dotfiles_uri = module.dotfiles.dotfiles_uri
}
```
## Setting a default dotfiles repository
You can set a default dotfiles repository for all users by setting the `default_dotfiles_uri` variable:
```tf
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
version = "1.0.29"
agent_id = coder_agent.example.id
default_dotfiles_uri = "https://github.com/coder/dotfiles"
}
```
@@ -0,0 +1,40 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("dotfiles", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
});
it("default output", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});
expect(state.outputs.dotfiles_uri.value).toBe("");
});
it("set a default dotfiles_uri", async () => {
const default_dotfiles_uri = "foo";
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
default_dotfiles_uri,
});
expect(state.outputs.dotfiles_uri.value).toBe(default_dotfiles_uri);
});
it("set custom order for coder_parameter", async () => {
const order = 99;
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
coder_parameter_order: order.toString(),
});
expect(state.resources).toHaveLength(2);
expect(state.resources[0].instances[0].attributes.order).toBe(order);
});
});
+91
View File
@@ -0,0 +1,91 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.12"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "default_dotfiles_uri" {
type = string
description = "The default dotfiles URI if the workspace user does not provide one"
default = ""
}
variable "dotfiles_uri" {
type = string
description = "The URL to a dotfiles repository. (optional, when set, the user isn't prompted for their dotfiles)"
default = null
}
variable "user" {
type = string
description = "The name of the user to apply the dotfiles to. (optional, applies to the current user by default)"
default = null
}
variable "coder_parameter_order" {
type = number
description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)."
default = null
}
variable "manual_update" {
type = bool
description = "If true, this adds a button to workspace page to refresh dotfiles on demand."
default = false
}
data "coder_parameter" "dotfiles_uri" {
count = var.dotfiles_uri == null ? 1 : 0
type = "string"
name = "dotfiles_uri"
display_name = "Dotfiles URL"
order = var.coder_parameter_order
default = var.default_dotfiles_uri
description = "Enter a URL for a [dotfiles repository](https://dotfiles.github.io) to personalize your workspace"
mutable = true
icon = "/icon/dotfiles.svg"
}
locals {
dotfiles_uri = var.dotfiles_uri != null ? var.dotfiles_uri : data.coder_parameter.dotfiles_uri[0].value
user = var.user != null ? var.user : ""
}
resource "coder_script" "dotfiles" {
agent_id = var.agent_id
script = templatefile("${path.module}/run.sh", {
DOTFILES_URI : local.dotfiles_uri,
DOTFILES_USER : local.user
})
display_name = "Dotfiles"
icon = "/icon/dotfiles.svg"
run_on_start = true
}
resource "coder_app" "dotfiles" {
count = var.manual_update ? 1 : 0
agent_id = var.agent_id
display_name = "Refresh Dotfiles"
slug = "dotfiles"
icon = "/icon/dotfiles.svg"
command = templatefile("${path.module}/run.sh", {
DOTFILES_URI : local.dotfiles_uri,
DOTFILES_USER : local.user
})
}
output "dotfiles_uri" {
description = "Dotfiles URI"
value = local.dotfiles_uri
}
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
DOTFILES_URI="${DOTFILES_URI}"
DOTFILES_USER="${DOTFILES_USER}"
if [ -n "$${DOTFILES_URI// }" ]; then
if [ -z "$DOTFILES_USER" ]; then
DOTFILES_USER="$USER"
fi
echo "✨ Applying dotfiles for user $DOTFILES_USER"
if [ "$DOTFILES_USER" = "$USER" ]; then
coder dotfiles "$DOTFILES_URI" -y 2>&1 | tee ~/.dotfiles.log
else
# The `eval echo ~"$DOTFILES_USER"` part is used to dynamically get the home directory of the user, see https://superuser.com/a/484280
# eval echo ~coder -> "/home/coder"
# eval echo ~root -> "/root"
CODER_BIN=$(which coder)
DOTFILES_USER_HOME=$(eval echo ~"$DOTFILES_USER")
sudo -u "$DOTFILES_USER" sh -c "'$CODER_BIN' dotfiles '$DOTFILES_URI' -y 2>&1 | tee '$DOTFILES_USER_HOME'/.dotfiles.log"
fi
fi
@@ -0,0 +1,62 @@
---
display_name: File Browser
description: A file browser for your workspace
icon: ../../../../.icons/filebrowser.svg
maintainer_github: coder
verified: true
tags: [helper, filebrowser]
---
# File Browser
A file browser for your workspace.
```tf
module "filebrowser" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
}
```
![Filebrowsing Example](../../.images/filebrowser.png)
## Examples
### Serve a specific directory
```tf
module "filebrowser" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
}
```
### Specify location of `filebrowser.db`
```tf
module "filebrowser" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
database_path = ".config/filebrowser.db"
}
```
### Serve from the same domain (no subdomain)
```tf
module "filebrowser" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.31"
agent_id = coder_agent.example.id
agent_name = "main"
subdomain = false
}
```
+123
View File
@@ -0,0 +1,123 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.17"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
variable "agent_name" {
type = string
description = "The name of the coder_agent resource. (Only required if subdomain is false and the template uses multiple agents.)"
default = null
}
variable "database_path" {
type = string
description = "The path to the filebrowser database."
default = "filebrowser.db"
validation {
# Ensures path leads to */filebrowser.db
condition = can(regex(".*filebrowser\\.db$", var.database_path))
error_message = "The database_path must end with 'filebrowser.db'."
}
}
variable "log_path" {
type = string
description = "The path to log filebrowser to."
default = "/tmp/filebrowser.log"
}
variable "port" {
type = number
description = "The port to run filebrowser on."
default = 13339
}
variable "folder" {
type = string
description = "--root value for filebrowser."
default = "~"
}
variable "share" {
type = string
default = "owner"
validation {
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
}
}
variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}
variable "slug" {
type = string
description = "The slug of the coder_app resource."
default = "filebrowser"
}
variable "subdomain" {
type = bool
description = <<-EOT
Determines whether the app will be accessed via it's own subdomain or whether it will be accessed via a path on Coder.
If wildcards have not been setup by the administrator then apps with "subdomain" set to true will not be accessible.
EOT
default = true
}
resource "coder_script" "filebrowser" {
agent_id = var.agent_id
display_name = "File Browser"
icon = "/icon/filebrowser.svg"
script = templatefile("${path.module}/run.sh", {
LOG_PATH : var.log_path,
PORT : var.port,
FOLDER : var.folder,
LOG_PATH : var.log_path,
DB_PATH : var.database_path,
SUBDOMAIN : var.subdomain,
SERVER_BASE_PATH : local.server_base_path
})
run_on_start = true
}
resource "coder_app" "filebrowser" {
agent_id = var.agent_id
slug = var.slug
display_name = "File Browser"
url = local.url
icon = "/icon/filebrowser.svg"
subdomain = var.subdomain
share = var.share
order = var.order
healthcheck {
url = local.healthcheck_url
interval = 5
threshold = 6
}
}
locals {
server_base_path = var.subdomain ? "" : format("/@%s/%s%s/apps/%s", data.coder_workspace_owner.me.name, data.coder_workspace.me.name, var.agent_name != null ? ".${var.agent_name}" : "", var.slug)
url = "http://localhost:${var.port}${local.server_base_path}"
healthcheck_url = "http://localhost:${var.port}${local.server_base_path}/health"
}
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
BOLD='\033[[0;1m'
printf "$${BOLD}Installing filebrowser \n\n"
# Check if filebrowser is installed
if ! command -v filebrowser &> /dev/null; then
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
fi
printf "🥳 Installation complete! \n\n"
printf "🛠️ Configuring filebrowser \n\n"
ROOT_DIR=${FOLDER}
ROOT_DIR=$${ROOT_DIR/\~/$HOME}
echo "DB_PATH: ${DB_PATH}"
export FB_DATABASE="${DB_PATH}"
# Check if filebrowser db exists
if [[ ! -f "${DB_PATH}" ]]; then
filebrowser config init 2>&1 | tee -a ${LOG_PATH}
filebrowser users add admin "" --perm.admin=true --viewMode=mosaic 2>&1 | tee -a ${LOG_PATH}
fi
filebrowser config set --baseurl=${SERVER_BASE_PATH} --port=${PORT} --auth.method=noauth --root=$ROOT_DIR 2>&1 | tee -a ${LOG_PATH}
printf "👷 Starting filebrowser in background... \n\n"
printf "📂 Serving $${ROOT_DIR} at http://localhost:${PORT} \n\n"
filebrowser >> ${LOG_PATH} 2>&1 &
printf "📝 Logs at ${LOG_PATH} \n\n"
@@ -0,0 +1,70 @@
---
display_name: Fly.io Region
description: A parameter with human region names and icons
icon: ../../../../.icons/fly.svg
maintainer_github: coder
verified: true
tags: [helper, parameter, fly.io, regions]
---
# Fly.io Region
This module adds Fly.io regions to your Coder template. Regions can be whitelisted using the `regions` argument and given custom names and custom icons with their respective map arguments (`custom_names`, `custom_icons`).
We can use the simplest format here, only adding a default selection as the `atl` region.
```tf
module "fly-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/fly-region/coder"
version = "1.0.2"
default = "atl"
}
```
![Fly.io Default](../../.images/flyio-basic.png)
## Examples
### Using region whitelist
The regions argument can be used to display only the desired regions in the Coder parameter.
```tf
module "fly-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/fly-region/coder"
version = "1.0.2"
default = "ams"
regions = ["ams", "arn", "atl"]
}
```
![Fly.io Filtered Regions](../../.images/flyio-filtered.png)
### Using custom icons and names
Set custom icons and names with their respective maps.
```tf
module "fly-region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/fly-region/coder"
version = "1.0.2"
default = "ams"
custom_icons = {
"ams" = "/emojis/1f90e.png"
}
custom_names = {
"ams" = "We love the Netherlands!"
}
}
```
![Fly.io custom icon and name](../../.images/flyio-custom.png)
## Associated template
Also see the Coder template registry for a [Fly.io template](https://registry.coder.com/templates/fly-docker-image) that provisions workspaces as Fly.io machines.
@@ -0,0 +1,32 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("fly-region", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {});
it("default output", async () => {
const state = await runTerraformApply(import.meta.dir, {});
expect(state.outputs.value.value).toBe("");
});
it("customized default", async () => {
const state = await runTerraformApply(import.meta.dir, {
default: "atl",
});
expect(state.outputs.value.value).toBe("atl");
});
it("region filter", async () => {
const state = await runTerraformApply(import.meta.dir, {
default: "atl",
regions: '["arn", "ams", "bos"]',
});
expect(state.outputs.value.value).toBe("");
});
});
+287
View File
@@ -0,0 +1,287 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.12"
}
}
}
variable "display_name" {
default = "Fly.io Region"
description = "The display name of the parameter."
type = string
}
variable "description" {
default = "The region to deploy workspace infrastructure."
description = "The description of the parameter."
type = string
}
variable "default" {
default = null
description = "The default region to use if no region is specified."
type = string
}
variable "mutable" {
default = false
description = "Whether the parameter can be changed after creation."
type = bool
}
variable "custom_names" {
default = {}
description = "A map of custom display names for region IDs."
type = map(string)
}
variable "custom_icons" {
default = {}
description = "A map of custom icons for region IDs."
type = map(string)
}
variable "regions" {
default = []
description = "List of regions to include for region selection."
type = list(string)
}
locals {
regions = {
"ams" = {
name = "Amsterdam, Netherlands"
gateway = true
paid_only = false
icon = "/emojis/1f1f3-1f1f1.png"
}
"arn" = {
name = "Stockholm, Sweden"
gateway = false
paid_only = false
icon = "/emojis/1f1f8-1f1ea.png"
}
"atl" = {
name = "Atlanta, Georgia (US)"
gateway = false
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"bog" = {
name = "Bogotá, Colombia"
gateway = false
paid_only = false
icon = "/emojis/1f1e8-1f1f4.png"
}
"bom" = {
name = "Mumbai, India"
gateway = true
paid_only = true
icon = "/emojis/1f1ee-1f1f3.png"
}
"bos" = {
name = "Boston, Massachusetts (US)"
gateway = false
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"cdg" = {
name = "Paris, France"
gateway = true
paid_only = false
icon = "/emojis/1f1eb-1f1f7.png"
}
"den" = {
name = "Denver, Colorado (US)"
gateway = false
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"dfw" = {
name = "Dallas, Texas (US)"
gateway = true
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"ewr" = {
name = "Secaucus, NJ (US)"
gateway = false
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"eze" = {
name = "Ezeiza, Argentina"
gateway = false
paid_only = false
icon = "/emojis/1f1e6-1f1f7.png"
}
"fra" = {
name = "Frankfurt, Germany"
gateway = true
paid_only = true
icon = "/emojis/1f1e9-1f1ea.png"
}
"gdl" = {
name = "Guadalajara, Mexico"
gateway = false
paid_only = false
icon = "/emojis/1f1f2-1f1fd.png"
}
"gig" = {
name = "Rio de Janeiro, Brazil"
gateway = false
paid_only = false
icon = "/emojis/1f1e7-1f1f7.png"
}
"gru" = {
name = "Sao Paulo, Brazil"
gateway = false
paid_only = false
icon = "/emojis/1f1e7-1f1f7.png"
}
"hkg" = {
name = "Hong Kong, Hong Kong"
gateway = true
paid_only = false
icon = "/emojis/1f1ed-1f1f0.png"
}
"iad" = {
name = "Ashburn, Virginia (US)"
gateway = true
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"jnb" = {
name = "Johannesburg, South Africa"
gateway = false
paid_only = false
icon = "/emojis/1f1ff-1f1e6.png"
}
"lax" = {
name = "Los Angeles, California (US)"
gateway = true
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"lhr" = {
name = "London, United Kingdom"
gateway = true
paid_only = false
icon = "/emojis/1f1ec-1f1e7.png"
}
"mad" = {
name = "Madrid, Spain"
gateway = false
paid_only = false
icon = "/emojis/1f1ea-1f1f8.png"
}
"mia" = {
name = "Miami, Florida (US)"
gateway = false
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"nrt" = {
name = "Tokyo, Japan"
gateway = true
paid_only = false
icon = "/emojis/1f1ef-1f1f5.png"
}
"ord" = {
name = "Chicago, Illinois (US)"
gateway = true
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"otp" = {
name = "Bucharest, Romania"
gateway = false
paid_only = false
icon = "/emojis/1f1f7-1f1f4.png"
}
"phx" = {
name = "Phoenix, Arizona (US)"
gateway = false
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"qro" = {
name = "Querétaro, Mexico"
gateway = false
paid_only = false
icon = "/emojis/1f1f2-1f1fd.png"
}
"scl" = {
name = "Santiago, Chile"
gateway = true
paid_only = false
icon = "/emojis/1f1e8-1f1f1.png"
}
"sea" = {
name = "Seattle, Washington (US)"
gateway = true
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"sin" = {
name = "Singapore, Singapore"
gateway = true
paid_only = false
icon = "/emojis/1f1f8-1f1ec.png"
}
"sjc" = {
name = "San Jose, California (US)"
gateway = true
paid_only = false
icon = "/emojis/1f1fa-1f1f8.png"
}
"syd" = {
name = "Sydney, Australia"
gateway = true
paid_only = false
icon = "/emojis/1f1e6-1f1fa.png"
}
"waw" = {
name = "Warsaw, Poland"
gateway = false
paid_only = false
icon = "/emojis/1f1f5-1f1f1.png"
}
"yul" = {
name = "Montreal, Canada"
gateway = false
paid_only = false
icon = "/emojis/1f1e8-1f1e6.png"
}
"yyz" = {
name = "Toronto, Canada"
gateway = true
paid_only = false
icon = "/emojis/1f1e8-1f1e6.png"
}
}
}
data "coder_parameter" "fly_region" {
name = "flyio_region"
display_name = var.display_name
description = var.description
default = (var.default != null && var.default != "") && ((var.default != null ? contains(var.regions, var.default) : false) || length(var.regions) == 0) ? var.default : null
mutable = var.mutable
dynamic "option" {
for_each = { for k, v in local.regions : k => v if anytrue([for d in var.regions : k == d]) || length(var.regions) == 0 }
content {
name = try(var.custom_names[option.key], option.value.name)
icon = try(var.custom_icons[option.key], option.value.icon)
value = option.key
}
}
}
output "value" {
value = data.coder_parameter.fly_region.value
}
@@ -0,0 +1,81 @@
---
display_name: GCP Region
description: Add Google Cloud Platform regions to your Coder template.
icon: ../../../../.icons/gcp.svg
maintainer_github: coder
verified: true
tags: [gcp, regions, parameter, helper]
---
# Google Cloud Platform Regions
This module adds Google Cloud Platform regions to your Coder template.
```tf
module "gcp_region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.12"
regions = ["us", "europe"]
}
resource "google_compute_instance" "example" {
zone = module.gcp_region.value
}
```
![GCP Regions](../../.images/gcp-regions.png)
## Examples
### Add only GPU zones in the US West 1 region
Note: setting `gpu_only = true` and using a default region without GPU support, the default will be set to `null`.
```tf
module "gcp_region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.12"
default = ["us-west1-a"]
regions = ["us-west1"]
gpu_only = false
}
resource "google_compute_instance" "example" {
zone = module.gcp_region.value
}
```
### Add all zones in the Europe West region
```tf
module "gcp_region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.12"
regions = ["europe-west"]
single_zone_per_region = false
}
resource "google_compute_instance" "example" {
zone = module.gcp_region.value
}
```
### Add a single zone from each region in US and Europe that has GPUs
```tf
module "gcp_region" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.12"
regions = ["us", "europe"]
gpu_only = true
single_zone_per_region = true
}
resource "google_compute_instance" "example" {
zone = module.gcp_region.value
}
```
@@ -0,0 +1,52 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("gcp-region", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {});
it("default output", async () => {
const state = await runTerraformApply(import.meta.dir, {});
expect(state.outputs.value.value).toBe("");
});
it("customized default", async () => {
const state = await runTerraformApply(import.meta.dir, {
regions: '["asia"]',
default: "asia-east1-a",
});
expect(state.outputs.value.value).toBe("asia-east1-a");
});
it("gpu only invalid default", async () => {
const state = await runTerraformApply(import.meta.dir, {
regions: '["us-west2"]',
default: "us-west2-a",
gpu_only: "true",
});
expect(state.outputs.value.value).toBe("");
});
it("gpu only valid default", async () => {
const state = await runTerraformApply(import.meta.dir, {
regions: '["us-west2"]',
default: "us-west2-b",
gpu_only: "true",
});
expect(state.outputs.value.value).toBe("us-west2-b");
});
it("set custom order for coder_parameter", async () => {
const order = 99;
const state = await runTerraformApply(import.meta.dir, {
coder_parameter_order: order.toString(),
});
expect(state.resources).toHaveLength(1);
expect(state.resources[0].instances[0].attributes.order).toBe(order);
});
});

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