profile
viewpoint
Vincent Vinnl The Netherlands https://vincenttunru.com Account mainly used for contributing to open source projects hosted on GitHub. My main open source work happens at GitLab: https://gitlab.com/VincentTunru

mozilla/fx-private-relay 882

Keep your email safe from hackers and trackers. Make an email alias with 1 click, and keep your address to yourself.

linkeddata/rdflib.js 485

Linked Data API for JavaScript

Vinnl/feeds 99

Example repository for creating your own RSS feeds using Feed me up, Scotty!

mozilla/fx-private-relay-add-on 18

Companion add-on for Firefox Relay. Keep your email safe from hackers and trackers. Make an email alias with one click, and keep your address to yourself.

mozilla-l10n/fx-private-relay-l10n 1

Strings for Firefox Relay Premium project

Vinnl/feed 1

A RSS, Atom and JSON Feed generator for Node.js, making content syndication simple and intuitive! 🚀

mozilla-l10n/fx-private-relay-add-on-l10n 0

Strings for the Relay Premium add-on project

Vinnl/arxiv-browse 0

NG browse

Vinnl/book 0

The Rust Programming Language

Vinnl/BulliedIntoBadScience.org 0

The Bullied Into Bad Science website

push eventmozilla/fx-private-relay

Vincent

commit sha 119dc262d0f035d3f6e2f52ec8ca3857b95e0b8b

Track manual anchor link changes in the Carousel

view details

push time in 14 minutes

push eventmozilla/fx-private-relay

Vincent

commit sha 2f57c1c4d7d8b46deb645e79620810aedae0cde7

Notify the user of bounced emails

view details

push time in 18 hours

PR opened mozilla/fx-private-relay

Reviewers
Back-end changes to allow website feature-parity through the API

New feature description

To allow the entire website to be generated based on data from the API, a couple of changes were needed. This PR includes those changes. Specifically, it:

  • exposes the user avatar as read-only (I added a new property to the Profile model, since this comes from FxA).
  • exposes whether the user has Premium as read-only (has_premium).
  • exposes data used in the bounce warning as read-only (next_email_try and bounce_status).
  • exposes the user's API token as read-only - this can be removed again once the add-on has its own OAuth flow.
  • makes the subdomain writable (note: there's a bit of validation in add_subdomain that I think does not get called now - can/should that be added as a validator on the field directly, or should it be called from django_rest_framework somehow?).
  • makes accounts/profile/subdomain callable as an API (so a local devserver hosting the front-end can call it).
  • adds an API endpoint for Users and exposes the user's email through the API.
  • adds an endpoint /accounts/profile/refresh that refetches data from FXA (i.e. called when ?fxa_refresh=1).
  • adds CORS headers when running with DEBUG that allows http://localhost:3000 to make API calls.

How to test

I've pushed a rebased version of the branch react-experiment that builds on top of this one. It has an updated README that demonstrates how to run the front-end (essentially: cd frontend; npm install; npm run watch in addition to running the backend).

Checklist

  • [x] l10n dependencies have been merged, if any.
  • [ ] All acceptance criteria are met. N/A
  • [x] All UI revisions follow the coding standards, and use Protocol tokens where applicable (see /static/scss/libs/protocol/css/includes/tokens/dist/index.scss).
  • [x] Commits in this PR are minimal and have descriptive commit messages.
+91 -4

0 comment

8 changed files

pr created time in 18 hours

push eventmozilla/fx-private-relay

Kaitlyn

commit sha 59e4d0cb6a13b4ff950bb22782b1c9d9923a56d2

Change contact tool tip to match menu

view details

Kaitlyn

commit sha 891f34ecb2703dbbb1f23e3250dea83672b86db4

Fix #956 - Shift main wrapper down when menu is toggled

view details

Kaitlyn

commit sha ff2b7e922e80e74cb7584bbac21d623e44bdcb6a

wip

view details

Kaitlyn

commit sha 901dafccfebce498c6085a2886bd8bb520f99382

wip

view details

Kaitlyn

commit sha 00703c5419cd3d2063db2a8a0814a1effe055456

add timeout function

view details

Kaitlyn

commit sha 905a06b64332f9edc484a3b4d5f6759939c47a02

add triple equal sign

view details

Kaitlyn

commit sha b280ec549ca858fd67986fdd3641cdbba6263af3

base 10, revise if statement

view details

Vincent

commit sha ea2c9bc253594bb3b856adef330fa6d27970da1a

Use country code for survey recruitment To be able to send out gift cards to survey participants, we'll limit the calls to participate in the survey to people in the US.

view details

luke crouch

commit sha b08d9570b30f09b6829a01ddf80d0f8f12dfb2f6

Merge pull request #1454 from mozilla/MPP-1401-interview-recruitment Use country code for survey recruitment

view details

Se Yeon Kim

commit sha e0f91a07bd1c1d0235f03434097a68b4c1af1e21

Return 400 for malformed to field

view details

Se Yeon Kim

commit sha 30acfec3db7e61235777183ac4cb84e051702ba4

Log exception from processing messages in DLQ Delete message after

view details

groovecoder

commit sha 60c577bedc2bfc38b278f2d39cbb72c5261f74fb

fix MPP-1512: send ga event for any premium user with "clicked-purchase" cookie FXA sends the subscription state change event directly to our back-end as soon as the user completes a purchase, and we process those events as soon as we get them. So, our check for "newly_premium" never evaluated to true, because the event had already updated the profile to premium by the time the user got back to the profile page. So instead, always send the "user_purchased_premium" event to GA when a user with a "clicked-purchase" cookie lands back on the profile page. Additionally, move the code that updates date_subscribed to the event-handling code.

view details

GitHub Actions - update submodules

commit sha 1891810ef7156a3538b71fe9d87a003b9038139b

Update submodules

view details

GitHub Actions - update submodules

commit sha fdc67b426c4bdc8016026cfb6b8a23de7f8f1d7a

Update submodules

view details

Se Yeon Kim

commit sha 54ca46b9471b3e7088c5f0ba1f246bebf97945de

Move message delete on finally clause

view details

Se Yeon Kim

commit sha a4008feab0bbe49c054402d57b44ee4c1d0f3c38

Merge pull request #1455 from mozilla/handle-errors-for-DLQ-cronjob-mpp-1533 Handle errors for dlq cronjob mpp 1533

view details

luke crouch

commit sha b283d971e8d2a42dd131bafe7fa02aa6a9be609f

Merge pull request #1457 from mozilla/fix-ga-user_purchased_premium-event-mpp-1512 fix MPP-1512: send ga event for any premium user with "clicked-purchase"

view details

GitHub Actions - update submodules

commit sha ee0a6d905fd34816d3940113ce75f99c07f9f01b

Update submodules

view details

GitHub Actions - update submodules

commit sha a8fef7f154bd3f66b1d925c8bcbfd6b223d6f7ee

Update submodules

view details

Vincent

commit sha af19882ffd54926f6849172b1f399310e56389f0

Add missing alt text to the Premium promo page

view details

push time in 18 hours

push eventmozilla/fx-private-relay

Vincent

commit sha b1fa656193f1c25c3b86832fbba061c1758b5206

Make user account data available to frontend

view details

Vincent

commit sha 7592e52fc2289a1392bed64bb1199c3d8775446c

Expose the user's avatar through the API

view details

Vincent

commit sha 6b55417c1c8060fd044c9e5c96fdc542dc57b281

Allow claiming a subdomain through the API

view details

Vincent

commit sha d12bc3583266bf91cd04a0eaf302af5b28e44c79

Expose the user's API token through the API This will allow the website to pass the token to the add-on. Later, we will modify the add-on to use a direct OAuth flow, which should be more secure.

view details

Vincent

commit sha a9937f07aef6676fa4f8bec2edb611fd42c8e822

Add API endpoint to refresh profile data An API consumer can call this endpoint to tell the back-end to fetch the latest profile data from Firefox Accounts, e.g. when the subscription flow brings the user back to the website, before fetching profile data.

view details

Vincent

commit sha 85dcc3882f8720a292587b532095bb3f26fb3850

Always log new subscription after "Purchase" click This applies the change from b283d971e8d2a42dd131bafe7fa02aa6a9be609f to the profile_refresh endpoint. Essentially, if the user has clicked a button to get Premium, and a refresh of profile data shows that the user has Premium, we count that as a new subscription - after all, those buttons should not be shown to users who already have Premium.

view details

Vincent

commit sha 2e0d0286642b1fa30b5d5eb1e498ccc05091b574

Locally allow the front-end to call the API When serving the front-end from a development server locally on http://localhost:3000, that single domain should be allowed to call the API.

view details

Vincent

commit sha 0e2b4a9d298361dfea8dc6646a6b898ae6802409

Expose bounce information through the API

view details

push time in 18 hours

push eventmozilla/fx-private-relay

Vincent

commit sha 646627a27cef0f92679faec75d24b31b87bdebde

Expose bounce information through the API

view details

push time in 19 hours

create barnchmozilla/fx-private-relay

branch : react-backend

created branch time in 21 hours

Pull request review commentmozilla/fx-private-relay-add-on

Prepare add-on for client-side rendering

 (async function () {-  // Get the api token from the account profile page-  const profileMainElement = document.querySelector("#profile-main");-  const apiToken = profileMainElement.dataset.apiToken;-  browser.storage.local.set({ apiToken });--  // API URL is ${RELAY_SITE_ORIGIN}/api/v1/-  const { relayApiSource } = await browser.storage.local.get("relayApiSource");--  const apiProfileURL = `${relayApiSource}/profiles/`;-  const apiRelayAddressesURL = `${relayApiSource}/relayaddresses/`;--  async function apiRequest(url, method = "GET", body = null, opts=null) {--    const cookieString =-      typeof document.cookie === "string" ? document.cookie : "";-    const cookieStringArray = cookieString-      .split(";")-      .map((individualCookieString) => individualCookieString.split("="))-      .map(([cookieKey, cookieValue]) => [-        cookieKey.trim(),-        cookieValue.trim(),-      ]);--    const [_csrfCookieKey, csrfCookieValue] = cookieStringArray.find(-      ([cookieKey, _cookieValue]) => cookieKey === "csrftoken"-    );+  const dahsboardInitializationObserver = new MutationObserver((mutations) => {+    mutations.forEach(mutation => {+      if (mutation.type !== "childList") {+        return;+      }+      mutation.addedNodes.forEach(node => {+        if (node.id !== "profile-main") {+          return;+        } -    browser.storage.local.set({ csrfCookieValue: csrfCookieValue });+        // Once an element `#profile-main` is added, the dashboard is initialized+        // with the data needed by the add-on; stop watching for futher changes,+        // and run once:+        dahsboardInitializationObserver.disconnect();+        run();+      });+    });+  });+  if (document.getElementById("profile-main") === null) {+    dahsboardInitializationObserver.observe(document.body, { childList: true, subtree: true });+  } else {+    run();+  } +  async function run() {+    // Get the api token from the account profile page+    const profileMainElement = document.querySelector("#profile-main");+    const apiToken = profileMainElement.dataset.apiToken;+    browser.storage.local.set({ apiToken }); -    const headers = new Headers();+    // API URL is ${RELAY_SITE_ORIGIN}/api/v1/+    const { relayApiSource } = await browser.storage.local.get("relayApiSource"); -    -    headers.set("X-CSRFToken", csrfCookieValue);-    headers.set("Content-Type", "application/json");-    headers.set("Accept", "application/json");-    -    if (opts && opts.auth) {-      const apiToken = await browser.storage.local.get("apiToken");-      headers.set("Authorization", `Token ${apiToken.apiToken}`);-    }+    const apiProfileURL = `${relayApiSource}/profiles/`;+    const apiRelayAddressesURL = `${relayApiSource}/relayaddresses/`; +    async function apiRequest(url, method = "GET", body = null, opts=null) { -    const response = await fetch(url, {-      mode: "same-origin",-      method,-      headers: headers,-      body,-    });+      const cookieString =+        typeof document.cookie === "string" ? document.cookie : "";+      const cookieStringArray = cookieString+        .split(";")+        .map((individualCookieString) => individualCookieString.split("="))+        .map(([cookieKey, cookieValue]) => [+          cookieKey.trim(),+          cookieValue.trim(),+        ]); -    answer = await response.json();-    return answer;-  }+      const [_csrfCookieKey, csrfCookieValue] = cookieStringArray.find(+        ([cookieKey, _cookieValue]) => cookieKey === "csrftoken"+      ); -  const serverProfileData = await apiRequest(apiProfileURL);+      browser.storage.local.set({ csrfCookieValue: csrfCookieValue }); -  browser.storage.local.set({-    profileID: parseInt(serverProfileData[0].id, 10),-    settings: {-      server_storage: serverProfileData[0].server_storage,-    },-  }); -  // Get the relay address objects from the addon storage-  const addonStorageRelayAddresses = await browser.storage.local.get(-    "relayAddresses"-  );--  const siteStorageEnabled = serverProfileData[0].server_storage;--  const addonRelayAddresses =-    Object.keys(addonStorageRelayAddresses).length === 0-      ? { relayAddresses: [] }-      : addonStorageRelayAddresses;--  // Check if user is premium-  const isPremiumUser = document-    .querySelector("body")-    .classList.contains("is-premium");-  browser.storage.local.set({ premium: isPremiumUser });--  // Loop over the addresses on the page-  const dashboardRelayAliasCards = document.querySelectorAll(-    "[data-relay-address]"-  );--  const localStorageRelayAddresses = [];--  // Get FXA Stuff-  const fxaSubscriptionsUrl = document.querySelector(-    "firefox-private-relay-addon-data"-  ).dataset.fxaSubscriptionsUrl;-  const premiumProdId = document.querySelector(-    "firefox-private-relay-addon-data"-  ).dataset.premiumProdId;-  const premiumPriceId = document.querySelector(-    "firefox-private-relay-addon-data"-  ).dataset.premiumPriceId;-  const aliasesUsedVal = document.querySelector(-    "firefox-private-relay-addon-data"-  ).dataset.aliasesUsedVal;-  const emailsForwardedVal = document.querySelector(-    "firefox-private-relay-addon-data"-  ).dataset.emailsForwardedVal;-  const emailsBlockedVal = document.querySelector(-    "firefox-private-relay-addon-data"-  ).dataset.emailsBlockedVal;-  const premiumSubdomainSet = document.querySelector(-    "firefox-private-relay-addon-data"-  ).dataset.premiumSubdomainSet;-  browser.storage.local.set({-    fxaSubscriptionsUrl,-    premiumProdId,-    premiumPriceId,-    aliasesUsedVal,-    emailsForwardedVal,-    emailsBlockedVal,-    premiumSubdomainSet,-  });+      const headers = new Headers(); -  // Loop through an array of aliases and see if any of them have descriptions or generated_for set.-  function aliasesHaveStoredMetadata(aliases) {-    for (const alias of aliases) {-      if (-        typeof alias.description === "string" &&-        alias.description.length > 0-      ) {-        return true;-      } -      if (typeof alias.generated_for === "string" && alias.generated_for.length > 0) {-        return true;+      headers.set("X-CSRFToken", csrfCookieValue);+      headers.set("Content-Type", "application/json");+      headers.set("Accept", "application/json");++      if (opts && opts.auth) {+        const apiToken = await browser.storage.local.get("apiToken");+        headers.set("Authorization", `Token ${apiToken.apiToken}`);       }-    }-  } -  // Loop through local storage aliases and sync any metadata they have with the server dataset-  async function sendMetaDataToServer(aliases) {-    for (const alias of aliases) {-      const body = {-        description: alias.description ?? "",-        generated_for: alias.generated_for ?? "",-      }; -      if (body.description.length > 0 || body.generated_for.length > 0) {-        await apiRequest(`${apiRelayAddressesURL}${alias.id}/`, "PATCH", JSON.stringify(body), {auth: true});-      }-    }-  }+      const response = await fetch(url, {+        mode: "same-origin",+        method,+        headers: headers,+        body,+      }); -  // Loop through the temp array that is about to be synced with the server dataset and -  // be sure it matches the local storage metadata dataset-  function getAliasesWithUpdatedMetadata(updatedAliases, prevAliases) {-    return prevAliases.map(prevAlias => {-      const updatedAlias = updatedAliases.find(otherAlias => otherAlias.id === prevAlias.id);-      return {-        ...prevAlias,-        description: updatedAlias.description.length > 0 ? updatedAlias.description : prevAlias.description,-        generated_for: updatedAlias.generated_for.length > 0 ? updatedAlias.generated_for : prevAlias.generated_for,-      };+      answer = await response.json();+      return answer;     }-  )}; -  if (siteStorageEnabled) {-    // Sync alias data from server page.-    // If local storage items exist AND have label metadata stored, sync it to the server.-    const serverRelayAddresses = await apiRequest(apiRelayAddressesURL);+    const serverProfileData = await apiRequest(apiProfileURL); -    // let usage: This data may be overwritten when merging the local storage dataset with the server set. -    let localCopyOfServerRelayAddresses = serverRelayAddresses;+    browser.storage.local.set({+      profileID: parseInt(serverProfileData[0].id, 10),+      settings: {+        server_storage: serverProfileData[0].server_storage,+      },+    }); -    // Check/cache local storage-    const { relayAddresses } = await browser.storage.local.get(+    // Get the relay address objects from the addon storage+    const addonStorageRelayAddresses = await browser.storage.local.get(       "relayAddresses"     ); -    if (-      relayAddresses &&-      relayAddresses.length > 0 &&-      aliasesHaveStoredMetadata(relayAddresses) && // Make sure there is meta data in the local dataset-      !aliasesHaveStoredMetadata(localCopyOfServerRelayAddresses) // Make sure there is no meta data in the server dataset-    ) {-      await sendMetaDataToServer(relayAddresses);-      localCopyOfServerRelayAddresses = getAliasesWithUpdatedMetadata(-        localCopyOfServerRelayAddresses,-        relayAddresses-      );-    }+    const siteStorageEnabled = serverProfileData[0].server_storage; -    browser.storage.local.set({ relayAddresses: localCopyOfServerRelayAddresses });-  } else {-    // Scrape alias data from Profile page (Local)+    const addonRelayAddresses =+      Object.keys(addonStorageRelayAddresses).length === 0+        ? { relayAddresses: [] }+        : addonStorageRelayAddresses; -    // await browser.runtime.sendMessage({-    //   method: "displayBrowserActionBadge",-    // });+    // Check if user is premium+    const isPremiumUser = document+      .querySelector("body")+      .classList.contains("is-premium");+    browser.storage.local.set({ premium: isPremiumUser }); -    const { relayAddresses } = await browser.storage.local.get(-      "relayAddresses"+    // Loop over the addresses on the page+    const dashboardRelayAliasCards = document.querySelectorAll(+      "[data-relay-address]"     );-    // Scrape data from /accounts/profile/ page-    for (const aliasCard of dashboardRelayAliasCards) {-      // Add the description (previoulsy domain) note from the addon storage to the page--      const aliasCardData = aliasCard.dataset;-      const aliasId = parseInt(aliasCardData.relayAddressId, 10);-      const addonRelayAddress = addonRelayAddresses.relayAddresses.filter(-        (address) => address.id == aliasId-      )[0];--      const defaultAliasLabelText = browser.i18n.getMessage(-        "profilePageDefaulAliasLabelText"-      ); -      // The labels (previously ONLY in local storage of the add-on) were set-      // as a string entry for "domain". With the new alias object from the server,-      // it already has an entry named "domain", which is an integer.-      // This variable checks for three truths:-      //   - Does the alias exists?-      //   - Does it have an entry for "domain"?-      //   - Is the entry NOT an integer?-      // If all three of these are true, this user has a legacy label stored locally-      // that needs to be ported to the "description" entry-      const storedLegacyAliasLabel =-        addonRelayAddress &&-        addonRelayAddress.hasOwnProperty("domain") &&-        !Number.isInteger(addonRelayAddress.domain);--      let storedAliasLabel =-        addonRelayAddress && addonRelayAddress.hasOwnProperty("description")-          ? addonRelayAddress.description-          : "";--      // Cache the generated_for alias attribute when updating local storage data.-      // Note that this data attribute only exists in aliases generated through the add-on-      let storedAliasGeneratedFor = addonRelayAddress?.generated_for ?? "";--      // This covers any legacy label field and remaps them.-      if (storedLegacyAliasLabel) {-        storedAliasLabel = addonRelayAddress.domain;+    const localStorageRelayAddresses = [];++    // Get FXA Stuff+    const fxaSubscriptionsUrl = document.querySelector(+      "firefox-private-relay-addon-data"+    ).dataset.fxaSubscriptionsUrl;+    const premiumProdId = document.querySelector(+      "firefox-private-relay-addon-data"+    ).dataset.premiumProdId;+    const premiumPriceId = document.querySelector(+      "firefox-private-relay-addon-data"+    ).dataset.premiumPriceId;+    const aliasesUsedVal = document.querySelector(+      "firefox-private-relay-addon-data"+    ).dataset.aliasesUsedVal;+    const emailsForwardedVal = document.querySelector(+      "firefox-private-relay-addon-data"+    ).dataset.emailsForwardedVal;+    const emailsBlockedVal = document.querySelector(+      "firefox-private-relay-addon-data"+    ).dataset.emailsBlockedVal;+    const premiumSubdomainSet = document.querySelector(+      "firefox-private-relay-addon-data"+    ).dataset.premiumSubdomainSet;+    browser.storage.local.set({+      fxaSubscriptionsUrl,+      premiumProdId,+      premiumPriceId,+      aliasesUsedVal,+      emailsForwardedVal,+      emailsBlockedVal,+      premiumSubdomainSet,+    });++    // Loop through an array of aliases and see if any of them have descriptions or generated_for set.+    function aliasesHaveStoredMetadata(aliases) {+      for (const alias of aliases) {+        if (+          typeof alias.description === "string" &&+          alias.description.length > 0+        ) {+          return true;+        }++        if (typeof alias.generated_for === "string" && alias.generated_for.length > 0) {+          return true;+        }       }+    }++    // Loop through local storage aliases and sync any metadata they have with the server dataset+    async function sendMetaDataToServer(aliases) {+      for (const alias of aliases) {+        const body = {+          description: alias.description ?? "",+          generated_for: alias.generated_for ?? "",+        }; -      // This covers any legacy siteOrigin field and remaps them.-      const storedLegacyAliasSiteOrigin = addonRelayAddress?.siteOrigin;-      if (storedLegacyAliasSiteOrigin) {-        storedAliasGeneratedFor = addonRelayAddress.generated_for;+        if (body.description.length > 0 || body.generated_for.length > 0) {+          await apiRequest(`${apiRelayAddressesURL}${alias.id}/`, "PATCH", JSON.stringify(body), {auth: true});+        }       }+    } -      const aliasLabelForm = aliasCard.querySelector(-        "form.relay-email-address-label-form"-      );-      const aliasLabelInput = aliasCard.querySelector(-        "input.relay-email-address-label"-      );+    // Loop through the temp array that is about to be synced with the server dataset and+    // be sure it matches the local storage metadata dataset+    function getAliasesWithUpdatedMetadata(updatedAliases, prevAliases) {+      return prevAliases.map(prevAlias => {+        const updatedAlias = updatedAliases.find(otherAlias => otherAlias.id === prevAlias.id);+        return {+          ...prevAlias,+          description: updatedAlias.description.length > 0 ? updatedAlias.description : prevAlias.description,+          generated_for: updatedAlias.generated_for.length > 0 ? updatedAlias.generated_for : prevAlias.generated_for,+        };+      }+    )}; -      const aliasLabelWrapper = aliasLabelForm.parentElement;-      aliasLabelWrapper.classList.add("show-label"); // Field is visible only to users who have the addon installed+    if (siteStorageEnabled) {+      // Sync alias data from server page.+      // If local storage items exist AND have label metadata stored, sync it to the server.+      const serverRelayAddresses = await apiRequest(apiRelayAddressesURL); -      aliasLabelInput.dataset.label = storedAliasLabel;+      // let usage: This data may be overwritten when merging the local storage dataset with the server set.+      let localCopyOfServerRelayAddresses = serverRelayAddresses; -      if (storedAliasLabel !== "") {-        aliasLabelInput.value = storedAliasLabel;-        aliasLabelWrapper.classList.add("user-created-label");-      } else {-        aliasLabelInput.placeholder = defaultAliasLabelText;+      // Check/cache local storage+      const { relayAddresses } = await browser.storage.local.get(+        "relayAddresses"+      );++      if (+        relayAddresses &&+        relayAddresses.length > 0 &&+        aliasesHaveStoredMetadata(relayAddresses) && // Make sure there is meta data in the local dataset+        !aliasesHaveStoredMetadata(localCopyOfServerRelayAddresses) // Make sure there is no meta data in the server dataset+      ) {+        await sendMetaDataToServer(relayAddresses);+        localCopyOfServerRelayAddresses = getAliasesWithUpdatedMetadata(+          localCopyOfServerRelayAddresses,+          relayAddresses+        );       } -      // eslint-disable-next-line quotes-      const forbiddenCharacters = `{}()=;'-<>"`;-      const showInputErrorMessage = (errorMessageContent) => {-        aliasLabelInput.classList.add("input-has-error");-        aliasLabelWrapper.querySelector(".input-error").textContent =-          errorMessageContent;-        aliasLabelWrapper.classList.add("show-input-error");-        return;-      };+      browser.storage.local.set({ relayAddresses: localCopyOfServerRelayAddresses });+    } else {+      // Scrape alias data from Profile page (Local) -      const pluralSingularErrorMessage = (badCharactersInValue) => {-        const newErrorMessage = browser.i18n.getMessage(-          "profilePageInvalidAliasCharactersError",-          Array.isArray(badCharactersInValue) ? badCharactersInValue.join(" ") : badCharactersInValue,+      // await browser.runtime.sendMessage({+      //   method: "displayBrowserActionBadge",+      // });++      const { relayAddresses } = await browser.storage.local.get(+        "relayAddresses"+      );+      // Scrape data from /accounts/profile/ page+      for (const aliasCard of dashboardRelayAliasCards) {+        // Add the description (previoulsy domain) note from the addon storage to the page++        const aliasCardData = aliasCard.dataset;+        const aliasId = parseInt(aliasCardData.relayAddressId, 10);+        const addonRelayAddress = addonRelayAddresses.relayAddresses.filter(+          (address) => address.id == aliasId+        )[0];++        const defaultAliasLabelText = browser.i18n.getMessage(+          "profilePageDefaulAliasLabelText"         );-        return newErrorMessage;-      }; -      const checkValueForErrors = (inputValue) => {-        // Catch copy/paste forbidden characters-        const forbiddenCharsInLabelValue = [];-        forbiddenCharacters.split("").forEach((badChar) => {-          if (-            inputValue.includes(badChar) &&-            !forbiddenCharsInLabelValue.includes(badChar)-          ) {-            forbiddenCharsInLabelValue.push(badChar);-          }-        });-        return forbiddenCharsInLabelValue;-      };--      aliasLabelInput.addEventListener("keydown", (e) => {-        // Limit keystrokes when the input has errors-        const keyChar = e.key;-        if (aliasLabelInput.classList.contains("input-has-error")) {-          const charactersToAllowWhileInputHasError = [-            "Tab",-            "Backspace",-            "ArrowLeft",-            "ArrowRight",-          ];-          if (!charactersToAllowWhileInputHasError.includes(keyChar)) {-            e.preventDefault();-            return;-          }+        // The labels (previously ONLY in local storage of the add-on) were set+        // as a string entry for "domain". With the new alias object from the server,+        // it already has an entry named "domain", which is an integer.+        // This variable checks for three truths:+        //   - Does the alias exists?+        //   - Does it have an entry for "domain"?+        //   - Is the entry NOT an integer?+        // If all three of these are true, this user has a legacy label stored locally+        // that needs to be ported to the "description" entry+        const storedLegacyAliasLabel =+          addonRelayAddress &&+          addonRelayAddress.hasOwnProperty("domain") &&+          !Number.isInteger(addonRelayAddress.domain);++        let storedAliasLabel =+          addonRelayAddress && addonRelayAddress.hasOwnProperty("description")+            ? addonRelayAddress.description+            : "";++        // Cache the generated_for alias attribute when updating local storage data.+        // Note that this data attribute only exists in aliases generated through the add-on+        let storedAliasGeneratedFor = addonRelayAddress?.generated_for ?? "";++        // This covers any legacy label field and remaps them.+        if (storedLegacyAliasLabel) {+          storedAliasLabel = addonRelayAddress.domain;         }-        // Show error message when forbidden keys are entered-        if (forbiddenCharacters.includes(keyChar)) {-          return showInputErrorMessage(-            `${keyChar} is not an allowed character`-          );++        // This covers any legacy siteOrigin field and remaps them.+        const storedLegacyAliasSiteOrigin = addonRelayAddress?.siteOrigin;+        if (storedLegacyAliasSiteOrigin) {+          storedAliasGeneratedFor = addonRelayAddress.generated_for;         }-      }); -      aliasLabelInput.addEventListener("keyup", (e) => {-        const keyChar = e.key;-        const forbiddenCharsInValue = checkValueForErrors(-          aliasLabelInput.value+        const aliasLabelForm = aliasCard.querySelector(+          "form.relay-email-address-label-form"+        );+        const aliasLabelInput = aliasCard.querySelector(+          "input.relay-email-address-label"         );-        if (-          forbiddenCharsInValue.length === 0 &&-          !forbiddenCharacters.includes(keyChar)-        ) {-          aliasLabelInput.classList.remove("input-has-error");-          aliasLabelWrapper.classList.remove("show-input-error");-          return;-        }-        if (forbiddenCharsInValue.length > 0) {-          return showInputErrorMessage(-            pluralSingularErrorMessage(forbiddenCharsInValue)-          );-        }-      }); -      const saveAliasLabel = () => {-        const newAliasLabel = aliasLabelInput.value;+        const aliasLabelWrapper = aliasLabelForm.parentElement;+        aliasLabelWrapper.classList.add("show-label"); // Field is visible only to users who have the addon installed -        // Don't save labels containing forbidden characters-        if (aliasLabelInput.classList.contains("input-has-error")) {-          return;-        }+        aliasLabelInput.dataset.label = storedAliasLabel; -        const forbiddenCharsInValue = checkValueForErrors(newAliasLabel);-        if (forbiddenCharsInValue.length > 0) {-          return showInputErrorMessage(-            pluralSingularErrorMessage(forbiddenCharsInValue)-          );+        if (storedAliasLabel !== "") {+          aliasLabelInput.value = storedAliasLabel;+          aliasLabelWrapper.classList.add("user-created-label");+        } else {+          aliasLabelInput.placeholder = defaultAliasLabelText;         } -        // Don't show saved confirmation message if the label hasn't changed-        if (newAliasLabel === aliasLabelInput.dataset.label) {+        // eslint-disable-next-line quotes+        const forbiddenCharacters = `{}()=;'-<>"`;+        const showInputErrorMessage = (errorMessageContent) => {+          aliasLabelInput.classList.add("input-has-error");+          aliasLabelWrapper.querySelector(".input-error").textContent =+            errorMessageContent;+          aliasLabelWrapper.classList.add("show-input-error");           return;-        }+        }; -        // Save new alias label-        const updatedRelayAddress = localStorageRelayAddresses.filter(-          (address) => address.id == aliasId-        )[0];-        updatedRelayAddress.description = newAliasLabel;-        browser.storage.local.set({-          relayAddresses: localStorageRelayAddresses,+        const pluralSingularErrorMessage = (badCharactersInValue) => {+          const newErrorMessage = browser.i18n.getMessage(+            "profilePageInvalidAliasCharactersError",+            Array.isArray(badCharactersInValue) ? badCharactersInValue.join(" ") : badCharactersInValue,+          );+          return newErrorMessage;+        };++        const checkValueForErrors = (inputValue) => {+          // Catch copy/paste forbidden characters+          const forbiddenCharsInLabelValue = [];+          forbiddenCharacters.split("").forEach((badChar) => {+            if (+              inputValue.includes(badChar) &&+              !forbiddenCharsInLabelValue.includes(badChar)+            ) {+              forbiddenCharsInLabelValue.push(badChar);+            }+          });+          return forbiddenCharsInLabelValue;+        };++        aliasLabelInput.addEventListener("keydown", (e) => {+          // Limit keystrokes when the input has errors+          const keyChar = e.key;+          if (aliasLabelInput.classList.contains("input-has-error")) {+            const charactersToAllowWhileInputHasError = [+              "Tab",+              "Backspace",+              "ArrowLeft",+              "ArrowRight",+            ];+            if (!charactersToAllowWhileInputHasError.includes(keyChar)) {+              e.preventDefault();+              return;+            }+          }+          // Show error message when forbidden keys are entered+          if (forbiddenCharacters.includes(keyChar)) {+            return showInputErrorMessage(+              `${keyChar} is not an allowed character`+            );+          }         }); -        // show placeholder text if the label is blank-        if (aliasLabelInput.value === "") {-          aliasLabelWrapper.classList.remove("user-created-label");-          aliasLabelInput.placeholder = defaultAliasLabelText;-        } else {-          aliasLabelWrapper.classList.add("user-created-label");-          aliasLabelWrapper.classList.add("show-saved-confirmation");-        }+        aliasLabelInput.addEventListener("keyup", (e) => {+          const keyChar = e.key;+          const forbiddenCharsInValue = checkValueForErrors(+            aliasLabelInput.value+          );+          if (+            forbiddenCharsInValue.length === 0 &&+            !forbiddenCharacters.includes(keyChar)+          ) {+            aliasLabelInput.classList.remove("input-has-error");+            aliasLabelWrapper.classList.remove("show-input-error");+            return;+          }+          if (forbiddenCharsInValue.length > 0) {+            return showInputErrorMessage(+              pluralSingularErrorMessage(forbiddenCharsInValue)+            );+          }+        });++        const saveAliasLabel = () => {+          const newAliasLabel = aliasLabelInput.value;++          // Don't save labels containing forbidden characters+          if (aliasLabelInput.classList.contains("input-has-error")) {+            return;+          }++          const forbiddenCharsInValue = checkValueForErrors(newAliasLabel);+          if (forbiddenCharsInValue.length > 0) {+            return showInputErrorMessage(+              pluralSingularErrorMessage(forbiddenCharsInValue)+            );+          }++          // Don't show saved confirmation message if the label hasn't changed+          if (newAliasLabel === aliasLabelInput.dataset.label) {+            return;+          } -        aliasLabelInput.dataset.label = newAliasLabel;-        setTimeout(() => {-          aliasLabelWrapper.classList.remove("show-saved-confirmation");-        }, 1000);-      };-      aliasLabelInput.addEventListener("focusout", () => {-        const isValid = aliasLabelForm.reportValidity();-        if (isValid) {+          // Save new alias label+          const updatedRelayAddress = localStorageRelayAddresses.filter(+            (address) => address.id == aliasId+          )[0];+          updatedRelayAddress.description = newAliasLabel;+          browser.storage.local.set({+            relayAddresses: localStorageRelayAddresses,+          });++          // show placeholder text if the label is blank+          if (aliasLabelInput.value === "") {+            aliasLabelWrapper.classList.remove("user-created-label");+            aliasLabelInput.placeholder = defaultAliasLabelText;+          } else {+            aliasLabelWrapper.classList.add("user-created-label");+            aliasLabelWrapper.classList.add("show-saved-confirmation");+          }++          aliasLabelInput.dataset.label = newAliasLabel;+          setTimeout(() => {+            aliasLabelWrapper.classList.remove("show-saved-confirmation");+          }, 1000);+        };+        aliasLabelInput.addEventListener("focusout", () => {+          const isValid = aliasLabelForm.reportValidity();+          if (isValid) {+            saveAliasLabel();+          }+        });+        aliasLabelForm?.addEventListener("submit", (event) => {+          event.preventDefault();           saveAliasLabel();-        }-      });-      aliasLabelForm?.addEventListener("submit", (event) => {-        event.preventDefault();-        saveAliasLabel();-        aliasLabelInput.blur();-      });+          aliasLabelInput.blur();+        }); -      // Get and store the relay addresses from the account profile page,-      // so they can be used later, even if the API endpoint is down+        // Get and store the relay addresses from the account profile page,+        // so they can be used later, even if the API endpoint is down -      const relayAddress = {-        id: aliasId,-        address: aliasCardData.relayAddress,-        description: storedAliasLabel,-        generated_for: storedAliasGeneratedFor,-      };+        const relayAddress = {+          id: aliasId,+          address: aliasCardData.relayAddress,+          description: storedAliasLabel,+          generated_for: storedAliasGeneratedFor,+        }; -      localStorageRelayAddresses.push(relayAddress);+        localStorageRelayAddresses.push(relayAddress);+      }++      if (localStorageRelayAddresses.length === 0) {+        // If we weren't able to scrape alias data from the page, that means+        // the React version of the website (with a different DOM structure)+        // has been deployed. Since the React version handles local storage of+        // label data by itself, we don't need to add our own listeners,+        // and we can just fetch label data from the API.+        const serverRelayAddresses = await apiRequest(apiRelayAddressesURL);+        localStorageRelayAddresses.push(...serverRelayAddresses);+      }

This part is also new, and is part of the second change (54e5c27666a6021ff86d08fc3954179b4fc23802): whereas we scrape the DOM to get aliases and their labels with the old website, we just fetch them from the API in the React-based version.

Vinnl

comment created time in a day

PullRequestReviewEvent

Pull request review commentmozilla/fx-private-relay-add-on

Prepare add-on for client-side rendering

  (async () => {   localStorage.setItem("fxRelayAddonInstalled", "true");-  document.querySelectorAll("firefox-private-relay-addon").forEach((el) => {+  document.querySelectorAll("firefox-private-relay-addon").forEach(async (el) => {     el.dataset.addonInstalled = "true";++    // In the server-rendered version of the website, the add-on would store alias labels+    // locally if server-side storage was disabled.+    // In the React version of the website, the website handles local storage itself.+    // However, to allow for seamless migration, this injects the labels stored in the add-on+    // into the website, so that it can copy those into its own storage.+    const localRandomAliasCache = (await browser.storage.local.get("relayAddresses")).relayAddresses;+    if (Array.isArray(localRandomAliasCache)) {+      const localLabels = localRandomAliasCache+        .filter(address => address.description.length > 0)+        .map(address => ({+          type: "random",+          id: Number.parseInt(address.id, 10),+          description: address.description,+          address: address.address,+        })+      );+      el.dataset.localLabels = JSON.stringify(localLabels);+    }

This is the second change referred to in the pull request description. After migrating to the React implementation of the website, that will allow us to get rid of all the label code inside the add-on (i.e. settings_refresh.js and a lot of code in get_profile_data.js).

Vinnl

comment created time in a day

PullRequestReviewEvent

Pull request review commentmozilla/fx-private-relay-add-on

Prepare add-on for client-side rendering

 (async function () {-  // Get the api token from the account profile page-  const profileMainElement = document.querySelector("#profile-main");-  const apiToken = profileMainElement.dataset.apiToken;-  browser.storage.local.set({ apiToken });--  // API URL is ${RELAY_SITE_ORIGIN}/api/v1/-  const { relayApiSource } = await browser.storage.local.get("relayApiSource");--  const apiProfileURL = `${relayApiSource}/profiles/`;-  const apiRelayAddressesURL = `${relayApiSource}/relayaddresses/`;--  async function apiRequest(url, method = "GET", body = null, opts=null) {--    const cookieString =-      typeof document.cookie === "string" ? document.cookie : "";-    const cookieStringArray = cookieString-      .split(";")-      .map((individualCookieString) => individualCookieString.split("="))-      .map(([cookieKey, cookieValue]) => [-        cookieKey.trim(),-        cookieValue.trim(),-      ]);--    const [_csrfCookieKey, csrfCookieValue] = cookieStringArray.find(-      ([cookieKey, _cookieValue]) => cookieKey === "csrftoken"-    );+  const dahsboardInitializationObserver = new MutationObserver((mutations) => {+    mutations.forEach(mutation => {+      if (mutation.type !== "childList") {+        return;+      }+      mutation.addedNodes.forEach(node => {+        if (node.id !== "profile-main") {+          return;+        } -    browser.storage.local.set({ csrfCookieValue: csrfCookieValue });+        // Once an element `#profile-main` is added, the dashboard is initialized+        // with the data needed by the add-on; stop watching for futher changes,+        // and run once:+        dahsboardInitializationObserver.disconnect();+        run();+      });+    });+  });+  if (document.getElementById("profile-main") === null) {+    dahsboardInitializationObserver.observe(document.body, { childList: true, subtree: true });+  } else {+    run();+  }

This diff looks intimidating, but the above is the only code that's new. Everything below is exactly the same as it was, except for being wrapped inside the run() function.

Vinnl

comment created time in a day

PullRequestReviewEvent

PR opened mozilla/fx-private-relay-add-on

Reviewers
Prepare add-on for client-side rendering

This adds two minor changes to the add-on to make it compatible with the React prototype I wrote:

  1. It wraps the code in get_profile_data.js in a function called run. If the element #profile-main exists at the time the script is executed (this is the current state: the HTML is generated by Django before being sent to the browser), that function is immediately executed, so the behaviour is exactly the same as now. If that element is not present, however (this is what happens when the React code is sent to the browser, which will then execute it to generate the DOM after get_profile_data.js is run), then a MutationObserver will be added, which watches whether #profile-main is added to the DOM, and only then executes run.
  2. In installation_indicator.js, it loads the locally stored alias labels (if any), and injects them into the website by setting the data-local-labels attribute on the <firefox-private-relay-addon> element. The React version of the website I wrote handles local storage of labels by itself, and this allows it to import any existing local labels.

How to test:

  • The add-on behaviour should not change when used with the website as it exists today.
  • The add-on should also continue working at it was when switching to the react-experiment branch of the main website.
+410 -357

0 comment

2 changed files

pr created time in a day

push eventmozilla/fx-private-relay-add-on

Kaitlyn

commit sha 0f8262738929a1885c9c456835a55888f7d88134

Resize Popup Panel Images for Localized Strings

view details

Vincent

commit sha 48aa5b96699c73775f0d195b340f67d022a9458a

Remove the `premiumEnabled` feature flag Premium has now been enabled for everyone.

view details

Kaitlyn Andres

commit sha 1742d0fcc91caea4a21ba056c6507e4b85ec9e45

Merge pull request #212 from mozilla/show-manage-alias-localized Resize Popup Panel Images for Localized Strings

view details

Vincent

commit sha 6d8f28d8560b4ad2ac4027c5d818b0d34d85c125

Link "Get unlimited aliases" to interstitial Instead of jumping directly into the purchasing flow, this first brings people to a page that explains what Premium is and what it will get them.

view details

Maxx Crawford

commit sha e8f17086296221be1ccd05c9ed3fe9176497d176

Update string paths for l10n updates (#251) * Update string paths for l10n updates * Update submodules

view details

GitHub Actions - update submodules

commit sha 58fa182d666c9aa0f8d43c7a3378ee4c4c6fd7f6

Update submodules

view details

GitHub Actions - update submodules

commit sha 6628a486e9fa38d6ac1453e99982c951185c6c4b

Update submodules

view details

Vincent

commit sha 03cd483475bae292e13a5c840ee4b3d779910f92

Create a deployment for every add-on push

view details

Maxx Crawford

commit sha c7f91f29ff00635aa916d4f096b680136d42626e

Chrome-compatible Extension (#249) * Allow add-on detection without layout shifts Instead of having the website wait 500ms for the add-on to inject data into it indicating that it is installed, this change will allow it to simply add an `is-hidden-with-addon` class, and then the add-on will inject CSS even before the JS is executed that will ensure that that element is not visible (or `is-visible-with-addon` for the inverse behaviour). This means we should no longer have the page jump around when the "Install the add-on" banner is displayed. * Inital commit – add polyfill plugin, remove blocking code so that add-on runs in both Firefox and Chrome * Add npm build commands * Style popup/panel for Chrome (and confirm Firefox compatibility) * Add additional meta data to create menu items * Refactor Loading Image * Chrome: Refactor how to locate target element when right-clicking on element to generate alias * Firefox: Fix edge case where reusing alias from context menu on accounts that are localStorage only was not filling email input correctly * Update version number * Fix #148 - Refactor font loading to be done at render time via JS. The font path is dynamically set to add-on path. * Update dev run commands Co-authored-by: Vincent <Vinnl@users.noreply.github.com> * Removed webextension-polyfill dependency as it was not actually in use. * Add code explanation comments, replace chrome. usage with browser., whitespace fixes * Add Chrome build steps to the README * Update author names * Update getURL paths, update submodules Co-authored-by: Vincent <git@vincenttunru.com> Co-authored-by: Vincent <Vinnl@users.noreply.github.com>

view details

GitHub Actions - update submodules

commit sha 7712a6227bcefd5904ee299a766ac0ac1d958434

Update submodules

view details

GitHub Actions - update submodules

commit sha d54456c87b6f2321b37e89a861805b68c7081749

Update submodules

view details

Vincent

commit sha 54e5c27666a6021ff86d08fc3954179b4fc23802

Make getting profile data work on React frontend The React frontend populates the DOM with the data when the client-side JavaScript runs, as opposed to it already being present in the HTML served by the server. Thus, this change wraps the existing code in a `run()` function that is executed once when the data is added to the DOM. For backwards compatibility, we also check on page load whether the data is already present, to ensure that people can update the add-on before the website is updated.

view details

Vincent

commit sha e099130eaf5cc8ce66c74c6cb127d008c4ba2b23

Inject locally stored labels into the website This should allow us to transition to having local storage of labels being handled by the website, rather than by the add-on (although it will still only be visible if the add-on is installed). Once local storage on the website has been deployed, we can remove all add-on code for label handling. This should make our code less brittle (since we're no longer relying on a particular DOM structure), and ensures that local and server storage are hitting similar code paths in terms of e.g. validation.

view details

push time in a day

issue commentmozilla/fx-private-relay

Label from previous entry showing in web-app after creating alias from firefox add-on

Ah sorry, stage is where our testers can test without using actual data. Thanks for verifying!

Oryoro

comment created time in a day

issue commentmozilla/fx-private-relay

Label from previous entry showing in web-app after creating alias from firefox add-on

@Oryoro Oh apologies, I linked just the issue number, but it's in a different repository. I meant this one: https://github.com/mozilla/fx-private-relay-add-on/issues/228

Oryoro

comment created time in a day

issue commentmozilla/fx-private-relay

Label from previous entry showing in web-app after creating alias from firefox add-on

Hi @Oryoro, is this the same issue as the one reported in #228?

Oryoro

comment created time in a day

push eventmozilla/fx-private-relay

Vincent

commit sha 19cdaa465e2c7db900b189b2fee23e8a8fc8835b

Always send a GA ping when someone hit Purchase Updating the React front-end to match with this: https://github.com/mozilla/fx-private-relay/pull/1457 Basically, the `has_premium` property is already updated when the user completes their purcahse, i.e. before our code runs. Thus, we now just send the GA event whenever the "clicked-purchase" cookie is set *and* the user has Premium (the buttons that set that cookie should not be shown to someone who already has Premium).

view details

Vincent

commit sha efccee02ec8bf0a74b345d6a9e1ded4209d7d075

Make "Contact us" tooltip match the menu's This reapplies a change to the Django template in React: https://github.com/mozilla/fx-private-relay/pull/1339

view details

push time in a day

push eventmozilla/fx-private-relay

Vincent

commit sha 098846403f44e79111b4c6c89eaf64b1d98eb728

Enforce labels for inline SVG icons See https://stackoverflow.com/a/4756461 and https://css-tricks.com/accessible-svgs/#2-inline-svg.

view details

Vincent

commit sha edbc1f5f0a50a12c18411050a5de4c7abb92a4ca

Add "Tips" popup at the bottom of the dashboard Currently it just shows a single tip (how to have a custom alias generated), but this might be expanded with more in the future.

view details

Vincent

commit sha 3c10b1b0ec3628698298997d6c06647558d43e9d

Remove country code from VPN link

view details

Vincent

commit sha 679dca37d2dced62a277fe6199a8f954d63259b7

Refresh profile data after puchasing Premium This used to be done while generating the view; there's now an API endpoint that does the same, and is called when the user initates the Premium purchase flow.

view details

push time in 4 days

created tagmozilla/fx-private-relay

tagv3.1.16

Keep your email safe from hackers and trackers. Make an email alias with 1 click, and keep your address to yourself.

created time in 4 days

delete branch mozilla/fx-private-relay-add-on

delete branch : MPP-1523-addon-deployment

delete time in 4 days

push eventmozilla/fx-private-relay-add-on

Vincent

commit sha 03cd483475bae292e13a5c840ee4b3d779910f92

Create a deployment for every add-on push

view details

push time in 4 days

PR merged mozilla/fx-private-relay-add-on

Reviewers
Create a deployment for every add-on push

This creates a GitHub Deployment whenever we push. If you then create a pull request, you can see it listed under "Environments", as below. Unfortunately, it doesn't seem to be possible to link to the artifact directly, so the "View deployment" link currently links to the Workflow run overview, which lists the add-on.

What it looks like when it's in progress:

image

And when built:

image

The "deployed" link is to the artifact:

image

I think if additional deployments are done after the PR is created, they are listed sa events below (in a similar style to "self-assigned this 3 minutes ago").

+73 -3

0 comment

1 changed file

Vinnl

pr closed time in 4 days

push eventmozilla/fx-private-relay-add-on

GitHub Actions - update submodules

commit sha 58fa182d666c9aa0f8d43c7a3378ee4c4c6fd7f6

Update submodules

view details

GitHub Actions - update submodules

commit sha 6628a486e9fa38d6ac1453e99982c951185c6c4b

Update submodules

view details

Vincent

commit sha cc33e2fb5347a93356dac4b52d80ea1be2d6c944

Create a deployment for every add-on push

view details

push time in 4 days

Pull request review commentmozilla/fx-private-relay-add-on

Create a deployment for every add-on push

 name: continuous prerelease to GitHub  on:   push:-    branches: [ main ]-+    branches: [ "*" ]   workflow_dispatch:  jobs:-  sign-tag-release:+  prepare-deployment:     runs-on: ubuntu-latest+    outputs:+      tag-slug: ${{ steps.determine-npm-tag.outputs.tag-slug }}+      deployment-id: ${{ fromJson(steps.create-deployment.outputs.result).data.id }}+    steps:+      - name: Create GitHub Deployment+        id: create-deployment+        uses: actions/github-script@v5+        with:+          script: |+            return await github.rest.repos.createDeployment({+              owner: context.repo.owner,+              repo: context.repo.repo,+              ref: context.sha,+              environment: "review",+              transient_environment: true,+              auto_merge: false,+              mediaType: {previews: ["flash", "ant-man"]},+              // The deployment runs in parallel with CI, so status checks will never have succeeded yet:+              required_contexts: [],+            });+ +  sign-tag-release:+    runs-on: ubuntu-latest+    needs: [prepare-deployment]     steps:+      - name: Mark GitHub Deployment as in progress+        id: start-deployment+        uses: actions/github-script@v5+        env:+          DEPLOYMENT_ID: "${{ needs.prepare-deployment.outputs.deployment-id }}"+        with:+          script: |+            return await github.rest.repos.createDeploymentStatus({+              owner: context.repo.owner,+              repo: context.repo.repo,+              deployment_id: process.env.DEPLOYMENT_ID,+              description: "Building add-on",+              environment: "review",+              log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,+              state: "in_progress",+              mediaType: {previews: ["flash", "ant-man"]},

I think they were listed in the docs back when I first implemented this; they're used for API previews. Specifically, they're for fairly minor features we're using: automatically labelling a Deployment as inactive and being able to list a Deployment as "in progress". I can't find any posts about those API's having graduated, but it also seems that the deployment still works just fine (including marking as "in progress") without them (d57000d), and they're no longer listed in the documentation examples, so I'll just keep them out.

Vinnl

comment created time in 4 days

PullRequestReviewEvent

push eventmozilla/fx-private-relay-add-on

Vincent

commit sha d57000d194477f69e953768588db7fd6380f6628

fixup! fixup! Create a deployment for every add-on push

view details

push time in 4 days

Pull request review commentmozilla/fx-private-relay-add-on

Create a deployment for every add-on push

 name: continuous prerelease to GitHub  on:   push:-    branches: [ main ]-+    branches: [ "*" ]   workflow_dispatch:  jobs:-  sign-tag-release:+  prepare-deployment:     runs-on: ubuntu-latest+    outputs:+      tag-slug: ${{ steps.determine-npm-tag.outputs.tag-slug }}

Whoops! I based this on the workflows I setup at my previous place, and this is a remnant of that. Removed in 35bdb1a.

Vinnl

comment created time in 4 days

PullRequestReviewEvent
more