Merge branch 'main' into feat-setup-experience

This commit is contained in:
Gabriel Hernandez 2024-10-11 13:37:33 +01:00
commit fc697a2bef
20 changed files with 177 additions and 95 deletions

View file

@ -4,8 +4,8 @@ on:
push:
branches:
- "main"
- "minor-*"
- "patch-*"
- "rc-minor-*"
- "rc-patch-*"
paths-ignore:
- "handbook/**"
- "website/**"

View file

@ -1,6 +1,6 @@
# Custom OS settings
In Fleet you can enforce OS settings like security restrictions, screen lock, Wi-Fi etc., on your your macOS, iOS, iPadOS, and Windows hosts using configuration or device profiles.
In Fleet you can enforce OS settings like security restrictions, screen lock, Wi-Fi etc., on your your macOS, iOS, iPadOS, and Windows hosts using configuration profiles.
## Enforce OS settings

View file

@ -48,7 +48,7 @@ To access and manage software in Fleet:
* Choose a file to upload. `.pkg`, `.msi`, `.exe`, and `.deb` files are supported.
> Software installer uploads will fail if Fleet is unable to extract information from the installer package such bundle ID and version number.
> Software installer uploads will fail if Fleet is unable to extract information from the installer package such as bundle ID and version number.
* To allow users to install the software from Fleet Desktop, check the “Self-service” checkbox.

View file

@ -0,0 +1 @@
* Fixes Orbit configuration endpoint 500s for Macs running Rapid Security Response macOS releases that are enrolled in OS major version enforcement

View file

@ -1983,18 +1983,14 @@ func (a *agent) batteries() []map[string]string {
count := rand.Intn(3) // return between 0 and 2 batteries
result := make([]map[string]string, count)
for i := range result {
health := "Good"
cycleCount := rand.Intn(2000)
switch {
case cycleCount > 1500:
health = "Poor"
case cycleCount > 1000:
health = "Fair"
}
max_capacity := 700 + rand.Intn(300) // between 700 and 1000 to ensure most batteries are healthy
cycleCount := rand.Intn(1200)
result[i] = map[string]string{
"serial_number": fmt.Sprintf("%04d", i),
"cycle_count": strconv.Itoa(cycleCount),
"health": health,
"serial_number": fmt.Sprintf("%04d", i),
"cycle_count": strconv.Itoa(cycleCount),
"max_capacity": strconv.Itoa(max_capacity),
"designed_capacity": "1000",
}
}
return result

View file

@ -16,21 +16,27 @@ const Install = ({
<svg
width={ICON_SIZES[size]}
height={ICON_SIZES[size]}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g
clipPath="url(#a)"
fillRule="evenodd"
clipRule="evenodd"
fill={COLORS[color]}
>
<path d="M8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12Zm0 2A8 8 0 1 0 8 0a8 8 0 0 0 0 16Z" />
<path d="M8 3.5a1 1 0 0 1 1 1v4.865l1.36-1.133a1 1 0 1 1 1.28 1.536l-3 2.5a1 1 0 0 1-1.28 0l-3-2.5a1 1 0 1 1 1.28-1.536L7 9.365V4.5a1 1 0 0 1 1-1Z" />
<g clipPath="url(#clip0_798_2)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16Z"
fill={COLORS[color]}
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.00003 3.5C8.55231 3.5 9.00003 3.94772 9.00003 4.5V9.36496L10.3598 8.23178C10.7841 7.87821 11.4147 7.93554 11.7682 8.35982C12.1218 8.78409 12.0645 9.41466 11.6402 9.76822L8.64021 12.2682C8.26936 12.5773 7.73069 12.5773 7.35984 12.2682L4.35984 9.76822C3.93556 9.41466 3.87824 8.78409 4.2318 8.35982C4.58537 7.93554 5.21593 7.87821 5.64021 8.23178L7.00003 9.36496V4.5C7.00003 3.94772 7.44774 3.5 8.00003 3.5Z"
fill={COLORS[color]}
/>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0 0h16v16H0z" />
<clipPath id="clip0_798_2">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>

View file

@ -24,7 +24,7 @@ const EmptyFleetAppsTable = () => (
header={"No items match the current search criteria"}
info={
<>
Can&apos; find app?{" "}
Can&apos;t find app?{" "}
<CustomLink
newTab
url="https://github.com/fleetdm/fleet/issues/new/choose"

View file

@ -4,42 +4,42 @@ import type { SVGProps } from "react";
const Brave = (props: SVGProps<SVGSVGElement>) => (
<svg fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path fill="#fff" d="M0 0h32v32H0z" />
<g clipPath="url(#a)">
<rect width="32" height="32" fill="white" />
<g clipPath="url(#clip0_1157_2708)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="m26.017 9.39.593-1.458s-.755-.809-1.67-1.727c-.917-.917-2.858-.377-2.858-.377l-2.21-2.51H12.111L9.9 5.827s-1.94-.54-2.857.377c-.916.918-1.671 1.727-1.671 1.727l.593 1.458-.755 2.158s2.22 8.417 2.48 9.445c.512 2.024.862 2.806 2.317 3.832 1.456 1.025 4.097 2.806 4.528 3.076.431.27.97.73 1.456.73.485 0 1.024-.46 1.455-.73.431-.27 3.072-2.051 4.527-3.076 1.456-1.026 1.806-1.808 2.318-3.832.26-1.028 2.48-9.445 2.48-9.445l-.755-2.158Z"
fill="url(#b)"
d="M26.0171 9.38964L26.61 7.93249C26.61 7.93249 25.8554 7.12296 24.9391 6.2055C24.0227 5.28804 22.0823 5.82772 22.0823 5.82772L19.8724 3.31738H15.9915H12.1106L9.9007 5.82772C9.9007 5.82772 7.96027 5.28804 7.04395 6.2055C6.12763 7.12296 5.37302 7.93249 5.37302 7.93249L5.96593 9.38964L5.21132 11.5484C5.21132 11.5484 7.43069 19.965 7.69076 20.9928C8.20282 23.0167 8.55318 23.7992 10.0085 24.8246C11.4638 25.85 14.105 27.6309 14.5362 27.9008C14.9674 28.1706 15.5064 28.6303 15.9915 28.6303C16.4766 28.6303 17.0156 28.1706 17.4468 27.9008C17.878 27.6309 20.5192 25.85 21.9745 24.8246C23.4298 23.7992 23.7802 23.0167 24.2922 20.9928C24.5523 19.965 26.7717 11.5484 26.7717 11.5484L26.0171 9.38964Z"
fill="url(#paint0_linear_1157_2708)"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M21.462 7.42s2.843 3.44 2.843 4.176c0 .735-.358.929-.717 1.311l-2.132 2.266c-.201.215-.62.54-.374 1.125.247.585.611 1.33.206 2.085-.405.755-1.099 1.259-1.544 1.175-.444-.083-1.489-.63-1.873-.879-.384-.25-1.6-1.255-1.6-1.64 0-.384 1.257-1.074 1.49-1.231.232-.157 1.293-.764 1.314-1.002.022-.239.014-.309-.3-.898-.312-.589-.876-1.375-.782-1.897.094-.523 1.003-.795 1.651-1.04.649-.246 1.897-.709 2.053-.78.156-.073.116-.141-.357-.186-.472-.045-1.813-.223-2.417-.055-.605.17-1.638.426-1.721.562-.084.136-.158.14-.072.61.086.47.528 2.723.571 3.123.043.4.127.665-.304.763-.43.099-1.156.27-1.405.27-.25 0-.975-.171-1.406-.27-.431-.098-.347-.363-.304-.763.043-.4.485-2.653.57-3.123.087-.47.013-.474-.07-.61-.084-.136-1.117-.393-1.722-.562-.604-.168-1.945.01-2.417.055-.473.045-.513.113-.357.185.156.072 1.404.535 2.053.78.648.246 1.557.518 1.65 1.04.095.523-.469 1.31-.782 1.898-.313.59-.32.66-.3.898.023.238 1.083.845 1.315 1.002.233.157 1.49.847 1.49 1.232 0 .384-1.216 1.39-1.6 1.639-.384.25-1.429.796-1.873.88-.445.083-1.139-.421-1.544-1.176-.405-.755-.04-1.5.206-2.085.247-.585-.173-.91-.375-1.125l-2.13-2.266c-.36-.382-.718-.576-.718-1.311 0-.736 2.843-4.176 2.843-4.176s2.398.459 2.722.459c.323 0 1.024-.27 1.67-.486.647-.216 1.079-.218 1.079-.218s.43.002 1.078.218c.646.216 1.347.486 1.67.486.324 0 2.723-.46 2.723-.46Zm-2.13 13.16c.175.11.068.319-.092.432-.16.113-2.314 1.784-2.523 1.968-.21.185-.517.49-.725.49-.21 0-.517-.305-.726-.49-.209-.184-2.363-1.855-2.523-1.968-.16-.113-.268-.321-.091-.432.176-.11.726-.388 1.486-.782.76-.393 1.706-.728 1.854-.728.147 0 1.094.335 1.854.728.76.394 1.31.672 1.485.782Z"
fill="#fff"
d="M21.4625 7.41984C21.4625 7.41984 24.305 10.8603 24.305 11.5956C24.305 12.331 23.9474 12.525 23.5879 12.9073C23.2284 13.2895 21.6582 14.959 21.4565 15.1734C21.2549 15.3878 20.8351 15.7128 21.082 16.2979C21.3289 16.883 21.6932 17.6275 21.2881 18.3826C20.883 19.1377 20.189 19.6417 19.7444 19.5584C19.2997 19.475 18.2554 18.9286 17.8713 18.679C17.4873 18.4294 16.2701 17.4243 16.2701 17.0397C16.2701 16.6552 17.5283 15.9647 17.7607 15.8078C17.9932 15.651 19.0535 15.0438 19.0752 14.8055C19.0969 14.5671 19.0886 14.4972 18.7757 13.9083C18.4628 13.3193 17.8993 12.5334 17.9931 12.0106C18.0869 11.4877 18.9957 11.2158 19.6442 10.9706C20.2926 10.7254 21.5413 10.2623 21.6972 10.1903C21.8532 10.1183 21.8129 10.0497 21.3404 10.0048C20.8679 9.95997 19.5271 9.78176 18.9226 9.95047C18.3181 10.1192 17.2854 10.3758 17.2017 10.512C17.118 10.6481 17.0443 10.6527 17.1302 11.1222C17.216 11.5917 17.6583 13.8447 17.7013 14.2448C17.7442 14.645 17.8282 14.9096 17.3973 15.0082C16.9663 15.1069 16.2408 15.2782 15.9915 15.2782C15.7422 15.2782 15.0167 15.1069 14.5858 15.0082C14.1548 14.9096 14.2388 14.645 14.2817 14.2448C14.3247 13.8447 14.7669 11.5917 14.8529 11.1222C14.9387 10.6527 14.8649 10.6481 14.7813 10.512C14.6977 10.3758 13.6649 10.1192 13.0604 9.95047C12.4559 9.78176 11.1151 9.95997 10.6426 10.0048C10.1701 10.0497 10.1298 10.1183 10.2858 10.1903C10.4418 10.2623 11.6904 10.7254 12.3388 10.9706C12.9873 11.2158 13.8961 11.4877 13.9899 12.0106C14.0837 12.5334 13.5202 13.3193 13.2073 13.9083C12.8944 14.4972 12.8861 14.5671 12.9078 14.8055C12.9295 15.0438 13.9898 15.651 14.2223 15.8078C14.4547 15.9647 15.7129 16.6552 15.7129 17.0397C15.7129 17.4243 14.4958 18.4294 14.1117 18.679C13.7277 18.9286 12.6833 19.475 12.2387 19.5584C11.794 19.6417 11.1 19.1377 10.6949 18.3826C10.2898 17.6275 10.6541 16.883 10.901 16.2979C11.1479 15.7128 10.7281 15.3878 10.5264 15.1734C10.3249 14.959 8.75462 13.2895 8.3951 12.9073C8.03558 12.525 7.67805 12.331 7.67805 11.5956C7.67805 10.8603 10.5206 7.41984 10.5206 7.41984C10.5206 7.41984 12.9192 7.87857 13.2426 7.87857C13.566 7.87857 14.2667 7.60873 14.9135 7.39285C15.5603 7.17698 15.9915 7.17542 15.9915 7.17542C15.9915 7.17542 16.4227 7.17698 17.0695 7.39285C17.7163 7.60873 18.4171 7.87857 18.7405 7.87857C19.0639 7.87857 21.4625 7.41984 21.4625 7.41984ZM19.3315 20.5804C19.5075 20.6907 19.4001 20.8986 19.2399 21.0121C19.0796 21.1255 16.9257 22.7955 16.7166 22.9801C16.5076 23.1647 16.2004 23.4696 15.9915 23.4696C15.7826 23.4696 15.4754 23.1647 15.2664 22.9801C15.0573 22.7955 12.9034 21.1255 12.7431 21.0121C12.5828 20.8986 12.4755 20.6907 12.6515 20.5804C12.8275 20.4701 13.378 20.1918 14.1376 19.7982C14.8971 19.4047 15.8438 19.0701 15.9915 19.0701C16.1392 19.0701 17.0859 19.4047 17.8455 19.7982C18.6051 20.1918 19.1555 20.4701 19.3315 20.5804Z"
fill="white"
/>
<path
d="m22.082 5.828-2.21-2.51H12.111L9.9 5.827s-1.94-.54-2.857.377c0 0 2.587-.233 3.477 1.215 0 0 2.398.459 2.722.459.323 0 1.024-.27 1.67-.486.647-.216 1.079-.218 1.079-.218s.43.002 1.078.218c.646.216 1.347.486 1.67.486.324 0 2.723-.46 2.723-.46.889-1.447 3.476-1.213 3.476-1.213-.916-.918-2.857-.378-2.857-.378Z"
fill="url(#c)"
d="M22.0823 5.82772L19.8724 3.31738H15.9915H12.1106L9.90071 5.82772C9.90071 5.82772 7.96028 5.28804 7.04396 6.2055C7.04396 6.2055 9.6312 5.97166 10.5206 7.41979C10.5206 7.41979 12.9192 7.87852 13.2426 7.87852C13.566 7.87852 14.2667 7.60868 14.9135 7.3928C15.5603 7.17693 15.9915 7.17537 15.9915 7.17537C15.9915 7.17537 16.4227 7.17693 17.0695 7.3928C17.7163 7.60868 18.4171 7.87852 18.7405 7.87852C19.0639 7.87852 21.4625 7.41979 21.4625 7.41979C22.3518 5.97166 24.9391 6.2055 24.9391 6.2055C24.0228 5.28804 22.0823 5.82772 22.0823 5.82772Z"
fill="url(#paint1_linear_1157_2708)"
/>
</g>
<defs>
<linearGradient
id="b"
x1="5.211"
id="paint0_linear_1157_2708"
x1="5.21132"
y1="1286.82"
x2="2161.25"
y2="1286.82"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#F50" />
<stop offset=".41" stopColor="#F50" />
<stop offset=".582" stopColor="#FF2000" />
<stop stopColor="#FF5500" />
<stop offset="0.409877" stopColor="#FF5500" />
<stop offset="0.581981" stopColor="#FF2000" />
<stop offset="1" stopColor="#FF2000" />
</linearGradient>
<linearGradient
id="c"
x1="45.49"
id="paint1_linear_1157_2708"
x1="45.4905"
y1="234.592"
x2="1796.55"
y2="234.592"
@ -48,11 +48,12 @@ const Brave = (props: SVGProps<SVGSVGElement>) => (
<stop stopColor="#FF452A" />
<stop offset="1" stopColor="#FF2000" />
</linearGradient>
<clipPath id="a">
<path
fill="#fff"
transform="translate(5.2 3.317)"
d="M0 0h21.6v25.365H0z"
<clipPath id="clip0_1157_2708">
<rect
width="21.6"
height="25.3651"
fill="white"
transform="translate(5.2 3.31738)"
/>
</clipPath>
</defs>

View file

@ -984,28 +984,21 @@ At the end of their first two weeks of onboarding at Fleet, every new team membe
Fleet prioritizes a [bias for action](https://fleetdm.com/handbook/company#ownership). If possible, apply onboarding feedback to the handbook and issue templates in realtime, during this call. This avoids backlogging tasks that may just get out of date before we get around to them anyway.
## Performance feedback
## Feedback
At Fleet, performance feedback is a continuous process. We give feedback (particularly negative) as soon as possible. Managers at Fleet will provide performance feedback [during scheduled 1:1 meetings](https://fleetdm.com/handbook/company/leadership#performance-feedback), if not sooner.
Feedback is how we improve. We encourage all Fleeties to share candid and helpful feedback with each other. We use the "4A" feedback pattern:
At Fleet, performance feedback is a continuous process. We give feedback (particularly negative) as soon as possible. Managers at Fleet will provide performance feedback [during scheduled 1:1 meetings](https://fleetdm.com/handbook/company/leadership#performance-feedback), if not sooner. When sharing feedback publicly, it's important to remember what feedback _is_:
- Feedback is something to share with the DRI, who has the context and mission to hear your feedback and decide what to do.
- Feedback is better shared in a way that doesn't ping the customer. Reading it doesn't help them.
- Feedback is ready to share. If it's just an opinion without a solution or an opinion about a customer's contribution without specifically saying why the contribution would make the product worse, it's not feedback that's ready to share.
- Feedback is how we improve. We encourage all Fleeties to share candid and helpful feedback with each other. We use the "4A" feedback pattern:
When _giving_ feedback:
1. Aim to assist
- Feedback must be given with positive intent.
- Feedback must be framed around how it can help the individual or the company.
2. Actionable
- Feedback must be actionable and focus on what the recipient can do differently.
1. **Aim to assist**: Feedback must be given with positive intent. Feedback must be framed around how it can help the individual or the company.
2. **Actionable**: Feedback must be actionable and focus on what the recipient can do differently.
When _receiving_ feedback:
3. Appreciate
- It is natural to feel defensive when hearing criticism.
- Rather than immediately reacting, listen carefully and be open-minded without becoming defensive or angry.
4. Accept or discard
- Listen and consider all feedback, then decide to accept or discard.
- The decision to react to the feedback is entirely up to the recipient.
3. **Appreciate**: It is natural to feel defensive when hearing criticism. Rather than immediately reacting, listen carefully and be open-minded without becoming defensive or angry.
4. **Accept or discard**: Listen and consider all feedback, then decide to accept or discard. The decision to react to the feedback is entirely up to the recipient.
> When delivering feedback, you can also use the [other person's personality type](https://docs.google.com/spreadsheets/d/1OSLn-ZCbGSjPusHPiR5dwQhheH1K8-xqyZdsOe9y7qc/edit#gid=0&range=AA1) as [a guide](https://drive.google.com/file/d/1iWHYJzc6WKdR95GBFAvjumouh0ej2DQM/view). For example, when delivering feedback to a ["type 1" personality](https://www.enneagraminstitute.com/type-1/), it helps to focus on minimizing the extent to which the person spirals into self-blame. In contrast, when delivering feedback to a ["type 7" personality](https://www.enneagraminstitute.com/type-7/), it is better to focus on being sensitive to the person's tendency to avoid negative emotions and reframe things positively.
@ -1705,6 +1698,9 @@ This glossary provides definitions to commonly used terms within our space.
The following stubs are included only to make links backward compatible.
##### Performance feedback
Please see 📖[handbook/company/communications#feedback](https://fleetdm.com/handbook/company/communications#feedback).
##### Website
Please see 📖[handbook/company/communications#fleetdm-com](https://fleetdm.com/handbook/company/communications#fleetdm-com).

View file

@ -7,6 +7,7 @@ This page covers the things managers and other leaders at Fleet need to know abo
## CEO flaws
[Openness](https://fleetdm.com/handbook/company#values) is important, and so I want to live that by sharing the flaws I know I have. Im fully responsible for improving the things below, listing them is no excuse.
These flaws are listed here publicly for two reasons. The first is so that people know it is _not just them_, but actually _my fault_. The second is so I can improve and be held accountable.
@ -34,11 +35,14 @@ These flaws are listed here publicly for two reasons. The first is so that peopl
> If you notice one of these flaws, and especially if you deliver feedback about it and don't feel heard, or you feel hurt, or you feel like I didn't "get it", please send me a link to this section of the handbook, or just interrupt me and give me [feedback in the moment](https://fleetdm.com/handbook/company/communications#performance-feedback). I will be extremely grateful, and value your bravery in pursuit of what's in the best interest of the company. (And if I don't, keep trying. I'll come crawling back. Promise.)
## Contact the CEO
**Still want to contact the CEO?** You can send `@mikermcneil` a DM in Fleet Slack, at-mention our CEO in the [#help-leadership channel](https://fleetdm.slack.com/archives/C02HWSTJ17Z), or [schedule time with the CEO](https://fleetdm.com/handbook/company/communications#schedule-time-with-the-ceo).
## CEO responsibilities
Ultimately, the CEO is responsible for the success or failure of the company. The CEO is the [directly responsible individual (DRI)](https://fleetdm.com/handbook/company/why-this-way#why-direct-responsibility) for pricing, tiers, the business model, human resources, legal counsel, signatures on all documents, and brand &amp; product marketing (brandfronts, pitchfronts, featurefronts, ICPs, personas, and targeting).
> **Note:** When the CEO is out of office, CEO responsibilities are either paused, delegated, or coordinated through the [Apprentice to the CEO](https://fleetdm.com/handbook/digital-experience#team) so they can be handled promptly. (It depends on the responsibility and the situation.)
@ -96,6 +100,7 @@ Departmental page structure:
## Key reviews
Every release cycle, each department leader discusses their [key performance indicators (KPIs)](https://docs.google.com/spreadsheets/d/1Hso0LxqwrRVINCyW_n436bNHmoqhoLhC8bcbvLPOs9A/edit#gid=0) (confidential) with the CEO. KPIs are numbers measuring results and everyday excellence, usually accompanied by time-bound goals.
In this meeting, the department leader discusses actual week-over-week progress toward the goals for a particular quarter with the CEO.
@ -111,8 +116,10 @@ At Fleet, we collaborate with [core team members](#creating-a-new-position), [co
> Are you a new fleetie joining the Digital Experience team? For Loom recordings demonstrating how to make offers, hire, onboard, and more please see [this classified Google Doc](https://docs.google.com/document/d/1fimxQguPOtK-2YLAVjWRNCYqs5TszAHJslhtT_23Ly0/edit).
### Consultants
#### Hiring a consultant
In addition to [core team members](#hiring-a-new-team-member), from time to time Fleet hires consultants who may work for only a handful of hours on short projects.
@ -133,6 +140,7 @@ Consultants [track time using the company's tools](#tracking-hours) and sign [Fl
To hire a consultant, [submit a new consultant onboarding request](https://github.com/fleetdm/confidential/issues/new?assignees=&labels=%23g-digital-experience&projects=&template=new-consultant-onboarding.md&title=New+US%2Finternational+consultant) to the Digital Experience team.
#### Who ISN'T a consultant?
If a consultant plans to work _more_ than 10 hours per week, or for _longer_ than 6 weeks, they should instead be hired as a [core team member](#hiring-a-new-team-member).
@ -149,6 +157,7 @@ Core team members:
Consultants aren't required to do any of those things.
#### Sending a consulting agreement
To send a consulting agreement, you will need to [submit a new consultant onboarding request](https://github.com/fleetdm/confidential/issues/new?assignees=&labels=%23g-digital-experience&projects=&template=new-consultant-onboarding.md&title=New+US%2Finternational+consultant) to the Digital Experience team. They will then peform the steps needed to bring aboard a new consultant.
@ -243,7 +252,9 @@ A completed open position entry should look something like this:
- _**Why bother with approvals?** We avoid cancelling or significantly changing a role after opening it. It hurts candidates too much. Instead, get the position approved first, before you start recruiting and interviewing. This gives you a sounding board and avoids misunderstandings._
### Approving a new position
When review is requested on a proposal to open a new position, the Apprentice to the CEO will complete the following steps when reviewing the pull request:
1. **Consider role and reporting structure:** Confirm the new row in "Fleeties" has a manager, job title, and department, that it doesn't have any corrupted spreadsheet formulas or formatting, and that the start date is set to the first Monday of the next month.
@ -265,30 +276,39 @@ When review is requested on a proposal to open a new position, the Apprentice to
> _**Note:** Most columns of the "Equity plan" are updated automatically when "Fleeties" is, based on the unique identifier of each row, like `🧑🚀890`. (Advisors have their own flavor of unique IDs, such as `🦉755`, which are defined in ["Advisors and investors"](https://docs.google.com/spreadsheets/d/15knBE2-PrQ1Ad-QcIk0mxCN-xFsATKK9hcifqrm0qFQ/edit).)_
### Recruiting
Fleet accepts job applications, but the company does not list positions on general purpose job boards. This prevents us being overwhelmed with candidates so we can fulfill our goal of responding promptly to every applicant.
This means that outbound recruiting, 3rd party recruiters, and references from team members are important aspect of the company's hiring strategy. Fleet's CEO is happy to assist with outreach, intros, and recruiting strategy for candidates.
### Recruiting
Fleet accepts job applications, but the company does not list positions on general purpose job boards. This prevents us being overwhelmed with candidates so we can fulfill our goal of responding promptly to every applicant. This means that outbound recruiting, 3rd party recruiters, and references from team members are important aspect of the company's hiring strategy. Fleet's CEO is happy to assist with outreach, intros, and recruiting strategy for candidates.
<img width="384" alt="image" src="https://github.com/fleetdm/fleet/assets/618009/ea3d3470-575d-4516-a19e-02abec3e1285">
#### Receiving job applications
Every job description page ends with a "call to action", including a link to the hiring manager's LinkedIn to apply for the job directly with the hiring manager. Fleet replies to all candidates within **1 business day** and always provides either a **rejection** or **decisive next steps**; even if the next step is just a promise. Hiring managers are encouraged to use [email/message templates](https://fleetdm.com/handbook/company/leadership#candidate-correspondence-email-templates) for consistency and efficiency.
#### Candidate correspondence email templates
Fleet uses [certain email templates](https://docs.google.com/document/d/1VAMWIH8o7_vH7lV9nM1wQCM4Vc3GxBRBDPK-mKO9HNk/edit?usp=sharing) when responding to candidates. This helps us live our value of [🔴 empathy](https://fleetdm.com/handbook/company#empathy) and helps the company meet the aspiration of replying to all applications within one business day.
### Hiring restrictions
#### Incompatible former employers
Fleet maintains a list of companies with whom Fleet has do-not-solicit terms that prevents us from making offers to employees of these companies. The list is in the Do Not Solicit tab of the [Digital Experience spreadsheet](https://docs.google.com/spreadsheets/d/1lp3OugxfPfMjAgQWRi_rbyL_3opILq-duHmlng_pwyo/edit#gid=0).
#### Incompatible locations
Fleet is unable to hire team members in some countries. See [this internal document](https://docs.google.com/document/d/1jHHJqShIyvlVwzx1C-FB9GC74Di_Rfdgmhpai1SPC0g/edit) for the list.
### Interviewing
> TODO: Rewrite this section for the hiring manager as our audience.
We're glad you're interested in joining the team!
@ -301,7 +321,9 @@ If you've been invited to "book with us," you'll have a Zoom meeting with the hi
Department specific interviewing instructions:
- [Engineering](https://fleetdm.com/handbook/engineering#interview-a-developer-candidate)
#### Hiring a new team member
This section is about the hiring process a new core team member, or fleetie.
> **_Note:_** _Employment classification isn't what makes someone a fleetie. Some Fleet team members are contractors and others are employees. The distinction between "contractor" and "employee" varies in different geographies, and the appropriate employment classification and agreement for any given team member and the place where they work is determined by Head of Digital Experience during the process of making an offer._
@ -339,6 +361,7 @@ Here are the steps hiring managers follow to get an offer out to a candidate:
### Making an offer
After receiving the interview packet, the Head of Digital Experience uses the following steps to make an offer:
<!-- For future use: some ready-to-go language around rebencharking compensation for cost of living: https://github.com/fleetdm/fleet/pulls/13499 -->
@ -362,6 +385,7 @@ After receiving the interview packet, the Head of Digital Experience uses the fo
- _Send_ the email.
5. **Process the offer response** The Head of Digital Experience will process the offer response by either creating a new ["Teammate pre-onboarding" issue](https://github.com/fleetdm/confidential/issues/new?assignees=&labels=%23g-digital-experience&projects=&template=pre-onboarding.md&title=Pre-onboarding%3A+______________________) and following the steps if the offer is accepted or notifying the stakeholders that the offer was not accepted and we should continue the search.
#### After an offer is accepted
The Head of Digital Experience will then follow the steps in the ["Teammate pre-onboarding"](https://github.com/fleetdm/confidential/issues/new?assignees=&labels=%23g-digital-experience&projects=&template=pre-onboarding.md&title=Pre-onboarding%3A+______________________) issue, which includes reaching out to the new team member within 1 business day from a separate email thread to get additional information as needed, prepare their agreement, add them to the company's payroll system, and get their new laptop and hardware security keys ordered so that everything is ready for them to start on their first day.
@ -399,25 +423,24 @@ CEO shadows join all meetings on the [CEO's calendar](https://calendar.google.co
## Tracking hours
Fleet asks US-based hourly contributors to track hours in Gusto, and contributors outside the US to track hours via Pilot.co.
This applies to anyone who gets paid by the hour, including consultants and hourly core team members of any employment classification, inside or outside of the US.
Fleet asks US-based hourly contributors to track hours in Gusto, and contributors outside the US to track hours via Pilot.co. This applies to anyone who gets paid by the hour, including consultants and hourly core team members of any employment classification, inside or outside of the US.
> _**Note:** If a contributor uses their own time-tracking process or tools, then it is OK to track the extra time spent tracking! Contributors at Fleet are evaluated based on their results, not the number of hours they work._
## Communicating departures
Although it's sad to see someone go, Fleet understands that not everything is meant to be forever [like open-source is](https://fleetdm.com/handbook/company/why-this-way#why-open-source). There are a few steps that the company needs to take to facilitate a departure.
1. **Departing team member's manager:** Inform the Head of Digital Experience about the departure via email and cc your manager. The Head of Digital Experience will coordinate the team member's last day, offboarding, and exit meeting.
3. **Digital Experience**: Will then create and begin completing [offboarding issue](https://github.com/fleetdm/classified/blob/main/.github/ISSUE_TEMPLATE/%F0%9F%9A%AA-offboarding-____________.md), to include coordinating team member's last day, offboarding, and exit meeting.
> After finding out about the departure, the Head of Digital Experience will post in #g-e to inform the E-group of the team member's departure, asking E-group members to inform any other managers on their teams.
1. **Departing team member's manager:** Create a private "#YYYY-offboarding-xxxxxx" Slack channel (replace "xxxxxx" with the Fleetie's name and YYYY with current year) for discussion and invite the CEO and Head of Digital Experience. Document any context about the departure via "FYI:" in the ["¶ 🗣 E-Group weekly" (confidential Google Doc)](https://docs.google.com/document/d/13fjq3T0bZGOUah9cqHVxngckv0EB2R24A3gfl5cH7eo/edit).
3. **Digital Experience**: The Head of Digital Experience will coordinate the team member's last day, offboarding, and exit meeting, then create and begin completing [offboarding issue](https://github.com/fleetdm/classified/blob/main/.github/ISSUE_TEMPLATE/%F0%9F%9A%AA-offboarding-____________.md), to include coordinating team member's last day, offboarding, and exit meeting. At the appropriate time, The Head of Digital Experience will post in #g-e to inform the E-group of the team member's departure, asking E-group members to inform any other managers on their teams.
4. **CEO**: The CEO will make an announcement during the "🌈 Weekly Update" post on Friday in the `#general` channel on Slack.
## Request a role change for a Fleetie
From time to time, someone's job title changes. The hiring manager can use the following steps to change someone's position:
1. Create Slack channel: Create a private "#YYYY-change-title-for-xxxxxx" Slack channel (where "xxxxxx" is the Fleetie's name and YYYY is the current year) for discussion and invite the CEO and Head of Digital Experience.
1. Create Slack channel: Create a private "#YYYY-change-title-for-xxxxxx" Slack channel (replace "xxxxxx" with the Fleetie's name and YYYY with current year) for discussion and invite the CEO and Head of Digital Experience.
2. At-mention the Head of Digital Experience in the new channel with any context regarding the title change. Share any related documents with the Head of Digital Experience and the CEO.
3. After getting approval from the [Head of People](https://fleetdm.com/handbook/digital-experience#team), the Digital Experience team will take the necessary steps to [change the fleetie's job title](https://fleetdm.com/handbook/digital-experience#change-a-fleeties-job-title).
@ -433,10 +456,7 @@ When it comes to performance feedback, [speak freely](https://fleetdm.com/handbo
3. When you meet with your manager for your 1:1, periodically provide an update on how each of your direct reports is doing at the top of your own "Performance management" section in your 1:1 agenda doc.
#### Stubs
##### Performance feedback
Please see 📖[handbook/company/leadership#delivering-performance-feedback](https://fleetdm.com/handbook/company/leadership#delivering-performance-feedback).
<meta name="maintainedBy" value="mikermcneil">

View file

@ -111,9 +111,54 @@ For Fleet's US contractors, running payroll is a manual process:
3. Adjust time frame to match current payroll period (the 27th through 26th of the month)
4. Sync hours and run contractor payroll.
### Update Finance department KPIs
Use the following steps to update the [💸Finance department KPIs](https://docs.google.com/spreadsheets/d/1Hso0LxqwrRVINCyW_n436bNHmoqhoLhC8bcbvLPOs9A/edit?gid=0#gid=0&range=BS:BS) by 5pm US central time every Friday.
**Runway**:
- Add sum of Brex cash account + SVB accounts. All Finance teammates should have personalized logins for each bank to view amounts.
- Send the CEO a Slack DM with the balance of each of Fleet's two bank accounts.
**Days to invoice customer**:
- Check the [invoice sent and payment received report]((https://fleetdm.lightning.force.com/lightning/r/Report/00O4x000007chpzEAA/view?queryScope=userFolders)) in Salesforce.
- Sort by close date, then check invoice dates for any recently closed deals and enter them in the finance spreadsheet and create new rows at the top of the 'time-to-invoice' tab in the spreadsheet for each new entry. If a company has not been invoiced yet (invoice date is blank) do not enter yet, but verify if an invoice is scheduled to be sent using the [Invoice status and upcoming bill dates](https://fleetdm.lightning.force.com/lightning/r/Report/00OUG0000010r8b2AA/view) report. If it doesn't appear here, create an issue on the finance board to investigate.
- Open the ["Time-to-invoice" tab](https://docs.google.com/spreadsheets/d/1lp3OugxfPfMjAgQWRi_rbyL_3opILq-duHmlng_pwyo/edit#gid=1835263594) in the [💸 Finance metrics spreadsheet](https://docs.google.com/spreadsheets/d/1lp3OugxfPfMjAgQWRi_rbyL_3opILq-duHmlng_pwyo/edit#gid=0). Enter the year and month + customer name in column A, then the # of days between the close date and the invoice date in column B. After entering new entries you will probably have to adjust the formula in column D to include the new cells in the average and make sure the formula includes no further back than the last three months of invoices.
**Overdue delinquent invoices**:
- Using the [Invoice status and upcoming bill dates](https://fleetdm.lightning.force.com/lightning/r/Report/00OUG0000010r8b2AA/view) report, check the invoice date of any entries without a payment received date against the payment terms.
- If the elapsed time exceeds the payment terms the invoice is overdue. If so, click on the link in the "Billing cycle name," and in the billing cycle object, update the billing status to "Overdue."
- Enter the number of invoices overdue in the KPI spreadsheet, and leave a comment on that cell with the names of the companies of overdue customers, if any.
- If any new customers are overdue, notify the CRO by posting a Slack message in the #g-sales channel and @ mention him.
**Commission payroll**:
- Make sure commission payroll was run on time by logging into [Gusto](https://www.gusto.com) and [Plane](https://plane.com/) to check for off-cycle payrolls in line with expected commission calculated in the [Commission calculator spreadsheet](https://docs.google.com/spreadsheets/d/1PuqUbfPGos87TfcHWgUd05TRJgQLlBmhyz1euj79m2A/edit?usp=sharing).
- Log the date the commission was paid out in the "Commission payroll" column of the KPI spreadsheet.
**Monthly accounting**:
- This will be the date that the monthly accounting issue for the prior month closes. If it has not yet closed, enter N/A.
**Days to pay**:
- Login to [Bill](https://www.bill.com).
- Under the "bills" tab on the menu bar on the left side, select "all bills".
- Sort by "created date."
- Compare the week's entries to the ["Time to pay"](https://docs.google.com/spreadsheets/d/1lp3OugxfPfMjAgQWRi_rbyL_3opILq-duHmlng_pwyo/edit#gid=1704186727) tab in the finance metrics spreadsheet. Insert new rows at the top for any bills that are not accounted for and enter the vendor name and the # of days between the invoice date and the created date in bill.com.
- Adjust the formula in column E as necessary to calculate the last 30 day average time to pay and enter the result in the KPI spreadsheet.
- If we have any invoices that have not yet been paid that are overdue, make an entry in the overdue vendor payments section and leave a comment in the cell identifying the vendor's name.
**Non-personnel monthly burn**:
- Copy the amount from the [numbers spreadsheet](https://docs.google.com/spreadsheets/d/1X-brkmUK7_Rgp7aq42drNcUg8ZipzEiS153uKZSabWc/edit#gid=1308221870&range=B3) and input in the cell for this week.
**SaaS metrics**:
- For "CAC", "CAC payback", "LTV" and "LTV:CAC" columns, drag the existing formula to this week's row.
> Note: the formula relies on inputs in other fields, so if those fields haven't received input yet, it will look odd. If formulas are still broken after inputs in the other columns are added, [create an issue on the Finance board](https://github.com/fleetdm/confidential/issues/new?assignees=&labels=%23g-finance&projects=&template=custom-request.md) noting which columns are affected. Once created, @ mention Head of Finance in the issue to bring awareness.
- For "Average customer age", pull the age in days from the [Salesforce "Account age" report](https://fleetdm.lightning.force.com/lightning/r/Report/00OUG0000012jwX2AQ/view), then convert to months by dividing the age in days by 30.417. Put the calculated number into the cell. This metric changes gradually up each week when no new deals close and no customer churns. Because we calculate the age of the customer based on the average lifetime deal length, expected behavior is that it will decrease when a new deal closes, but not drastically (as even a 1-year deal will still add 12 months to the average age).
### Create an invoice
To create a new invoice for a Fleet customer, follow these steps:
1. Go to the [invoice folder in google drive](https://drive.google.com/drive/folders/11limC_KQYNYQPApPoXN0CplHo_5Qgi2b?usp=drive_link).
1. Go to the [invoice folder in Google Drive](https://drive.google.com/drive/folders/11limC_KQYNYQPApPoXN0CplHo_5Qgi2b?usp=drive_link).
2. Create a copy of the invoice template, and title the copy `[invoice number] Fleet invoice - [customer name]`.
- The invoice number follows the format of `YYMMDD[daily issued invoice number]`, where the daily issued invoice number should equal `01` if it's the first invoice issued that day, `02` if it's the second, etc.
3. Edit the new invoice to reflect details from the signed subscription agreement (and PO if required).
@ -283,10 +328,6 @@ Fleet pays its vendors in less than 15 business days in most cases. All invoices
- Once cancelled, update the recurring expenses section of [The Numbers](https://docs.google.com/spreadsheets/d/1X-brkmUK7_Rgp7aq42drNcUg8ZipzEiS153uKZSabWc/edit#gid=2112277278) to reflect the cancellation by changing the projected monthly burn in column G to $0 and adding "CANCELLED" in front of the vendor's name in column C.
### Update weekly KPIs
- Create the weekly update issue from the template in ZenHub every Friday and update the [KPIs for finance](https://docs.google.com/spreadsheets/d/1Hso0LxqwrRVINCyW_n436bNHmoqhoLhC8bcbvLPOs9A/edit#gid=0) by 5pm US central time.
## Rituals
The following table lists this department's rituals, frequency, and Directly Responsible Individual (DRI).

View file

@ -5,6 +5,22 @@ The backend software patterns that we follow in Fleet.
> NOTE: There are always exceptions to the rules, but we try to follow these patterns as much as possible unless a specific use case calls
> for something else. These should be discussed within the team and documented before merging.
Table of Contents
- [API Inputs](#api-inputs)
- [MySQL](#mysql)
## API Inputs
### Input preprocessing and validation
Validate API inputs and return a 4XX status code if invalid. If you did not do authorization checking before failing validation, skip the authorization check with `svc.authz.SkipAuthorization(ctx)`.
Inputs corresponding to sortable or indexed DB fields should be preprocessed (trim spaces, normalize Unicode, etc.). Use utility method `fleet.Preprocess(input string) string`. [Backend sync where discussed](https://us-65885.app.gong.io/call?id=4055688254267958899).
### JSON unmarshaling
`PATCH` API calls often need to distinguish between a field being set to `null` and a field not being present in the JSON. Use the structs from `optjson` package to handle this. [Backend sync where discussed](https://us-65885.app.gong.io/call?id=4055688254267958899). [JSON unmarshaling article and example](https://victoronsoftware.com/posts/go-json-unmarshal/).
## MySQL
Use high precision for all time fields. Precise timestamps make sure that we can accurately track when records were created and updated,

View file

@ -2,6 +2,7 @@ package fleet
import (
"fmt"
"regexp"
"strings"
"github.com/Masterminds/semver"
@ -34,6 +35,7 @@ func (os OperatingSystem) IsWindows() bool {
}
var macOSNudgeLastVersion = semver.MustParse("14")
var macOSRapidSecurityResponseVersionSuffix = regexp.MustCompile(` \([a-z]\)`)
// RequiresNudge returns whether the target platform is darwin and
// below version 14. Starting at macOS 14 nudge is no longer required,
@ -43,7 +45,8 @@ func (os *OperatingSystem) RequiresNudge() (bool, error) {
return false, nil
}
version, err := semver.NewVersion(os.Version)
// strip Rapid Security Response suffix (e.g. version 13.3.7 (a)) if any
version, err := semver.NewVersion(macOSRapidSecurityResponseVersionSuffix.ReplaceAllString(os.Version, ``))
if err != nil {
return false, fmt.Errorf("parsing macos version \"%s\": %w", os.Version, err)
}

View file

@ -35,8 +35,12 @@ func TestOperatingSystemRequiresNudge(t *testing.T) {
{platform: "darwin", parseError: true},
{platform: "darwin", version: "12.0.9", requiresNudge: true},
{platform: "darwin", version: "11", requiresNudge: true},
{platform: "darwin", version: "13.3.1 (a)", requiresNudge: true},
{platform: "darwin", version: "13.4.1 (c)", requiresNudge: true},
{platform: "darwin", version: "14.0"},
{platform: "darwin", version: "14.3.2"},
{platform: "darwin", version: "15.0.1"},
{platform: "darwin", version: "15.0.1 (a)"},
{platform: "windows"},
{platform: "windows", version: "12.2"},
{platform: "windows", version: "15.4"},

View file

@ -17,7 +17,7 @@ This requires:
The script will check that each of these are installed and available before running
## Before running the script
## Before publishing the release
Make sure all tickets are tagged with the correct milestone.
@ -30,13 +30,14 @@ For example no tickets still in Ready / In Progress should be in the milestone w
Example:
```
# Build release candidate and changelogs and QA ticket
# git pull main locally
./tools/release/publish_release.sh -m
# Do QA until ready to release
# - QA is passed on all teams and ready for release
# - Merge changelog and versions update PR into main
# - git pull main locally with the changelog as the latest commit
# - Merge changelog and versions update PR into RC branch and main
# - git pull RC branch locally with the changelog as the latest commit
# Tag minor
./tools/release/publish_release.sh -mg

View file

@ -661,9 +661,9 @@ target_milestone="${next_ver:1}"
# 79
target_milestone_number=$(gh api repos/:owner/:repo/milestones | jq -r ".[] | select(.title==\"$target_milestone\") | .number")
# patch-fleet-v4.48.0
target_branch="patch-fleet-$next_ver"
target_branch="rc-patch-fleet-$next_ver"
if [[ "$minor" == "true" ]]; then
target_branch="minor-fleet-$next_ver"
target_branch="rc-minor-fleet-$next_ver"
fi
# fleet-v4.48.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -32,7 +32,6 @@ parasails.registerComponent('logoCarousel', {
<div purpose="logo-carousel">
<div purpose="logo-row" class="d-flex flex-row align-items-center" :class="[isIosThirteen ? 'ios-13-scroll-animation' : isSafariThirteen ? 'safari-13-scroll-animation' : '']">
<img alt="Notion logo" src="/images/logo-notion-68x32@2x.png">
<img alt="Pinterest logo" src="/images/logo-pinterest-98x32@2x.png">
<img alt="Gusto logo" src="/images/logo-gusto-64x32@2x.png">
<img alt="Epic Games logo" style="height: 32px" src="/images/logo-epic-games-28x32@2x.png">
<img alt="Rivian logo" src="/images/logo-rivian-120x32@2x.png">
@ -49,7 +48,6 @@ parasails.registerComponent('logoCarousel', {
</div>
<div purpose="logo-row" class="d-flex flex-row align-items-center" :class="[isIosThirteen ? 'ios-13-scroll-animation' : isSafariThirteen ? 'safari-13-scroll-animation' : '']">
<img alt="Notion logo" src="/images/logo-notion-68x32@2x.png">
<img alt="Pinterest logo" src="/images/logo-pinterest-98x32@2x.png">
<img alt="Gusto logo" src="/images/logo-gusto-64x32@2x.png">
<img alt="Epic Games logo" style="height: 32px" src="/images/logo-epic-games-28x32@2x.png">
<img alt="Rivian logo" src="/images/logo-rivian-120x32@2x.png">

View file

@ -177,8 +177,7 @@
<div purpose="logos" class="flex-column flex-wrap align-items-center w-100">
<div purpose="logo-row" class="d-flex flex-row justify-content-between">
<img alt="Notion logo" src="/images/logo-notion-68x32@2x.png">
<img alt="Pinterest logo" src="/images/logo-pinterest-98x32@2x.png">
<img alt="Gusto logo" src="/images/logo-gusto-64x32@2x.png">
<img alt="Atlassian logo" src="/images/logo-atlassian-140x32@2x.png">
<img alt="Epic Games logo" src="/images/logo-epic-games-28x32@2x.png">
</div>
<div purpose="logo-row" class="d-flex flex-row flex-wrap justify-content-between">
@ -188,7 +187,7 @@
</div>
<div purpose="logo-row" class="d-flex flex-row justify-content-between">
<img alt="Uber logo" src="/images/logo-uber-65x32@2x.png">
<img alt="Atlassian logo" src="/images/logo-atlassian-140x32@2x.png">
<img alt="Gusto logo" src="/images/logo-gusto-64x32@2x.png">
<img alt="Reddit logo" src="/images/logo-reddit-80x32@2x.png">
</div>
<div purpose="logo-row" class="d-flex flex-row justify-content-between mb-0">

View file

@ -168,7 +168,7 @@
<!-- <span purpose="table-new-badge" class="ml-2">New</span> -->
</div>
<div purpose="fleet-column">
<img style="height: 14px; width: 14px;" alt="coming soon" src="/images/icon-coming-soon-14x14@2x.png">
<img style="height: 14px; width: 14px;" alt="checkmark" purpose="checkmark" src="/images/icon-checkmark-green-16x16@2x.png">
</div>
<div purpose="comparison-column">
<img class="mx-auto" alt="checkmark" purpose="checkmark" src="/images/icon-checkmark-green-16x16@2x.png">
@ -179,7 +179,7 @@
<div purpose="table-row">
<div purpose="feature-name"><p>Automated device compliance</p></div>
<div purpose="fleet-column">
<img style="height: 14px; width: 14px;" alt="coming soon" src="/images/icon-coming-soon-14x14@2x.png">
<img style="height: 14px; width: 14px;" alt="checkmark" purpose="checkmark" src="/images/icon-checkmark-green-16x16@2x.png">
</div>
<div purpose="comparison-column">
<img class="mx-auto" alt="checkmark" purpose="checkmark" src="/images/icon-checkmark-green-16x16@2x.png">
@ -242,7 +242,7 @@
<div purpose="feature-table-row">
<div>Fleet</div>
<div purpose="feature-status">
<img style="height: 14px; width: 14px;" alt="coming soon" src="/images/icon-coming-soon-14x14@2x.png">
<img style="height: 14px; width: 14px;" alt="checkmark" purpose="checkmark" src="/images/icon-checkmark-green-16x16@2x.png">
</div>
</div>
<div purpose="feature-table-row" v-if="comparisonMode === 'omnissa'">
@ -351,7 +351,7 @@
<div purpose="feature-table-row">
<div>Fleet</div>
<div purpose="feature-status">
<img style="height: 14px; width: 14px;" alt="coming soon" src="/images/icon-coming-soon-14x14@2x.png">
<img style="height: 14px; width: 14px;" alt="checkmark" purpose="checkmark" src="/images/icon-checkmark-green-16x16@2x.png">
</div>
</div>
<div purpose="feature-table-row" v-if="comparisonMode === 'omnissa'">