diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml index d245073cc3ab..4367100d4559 100644 --- a/.github/workflows/compile_changelogs.yml +++ b/.github/workflows/compile_changelogs.yml @@ -2,55 +2,51 @@ name: Compile changelogs on: schedule: - - cron: "0 * * * *" - workflow_dispatch: + - cron: "0 0 * * *" jobs: compile: name: "Compile changelogs" runs-on: ubuntu-20.04 steps: - - name: "Check for CHANGELOG_ENABLER secret and pass true to output if it exists to be checked by later steps" + - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps" id: value_holder env: - CHANGELOG_ENABLER: ${{ secrets.CHANGELOG_ENABLER }} + ENABLER_SECRET: ${{ secrets.CHANGELOG_ENABLER }} run: | unset SECRET_EXISTS - if [ -n $CHANGELOG_ENABLER ]; then SECRET_EXISTS='true' ; fi - echo ::set-output name=CL_ENABLED::${SECRET_EXISTS} + if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi + echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS" - name: "Setup python" - if: steps.value_holder.outputs.CL_ENABLED + if: steps.value_holder.outputs.ACTIONS_ENABLED uses: actions/setup-python@v1 with: - python-version: "3.9" + python-version: '3.x' - name: "Install deps" - if: steps.value_holder.outputs.CL_ENABLED + if: steps.value_holder.outputs.ACTIONS_ENABLED run: | python -m pip install --upgrade pip python -m pip install pyyaml sudo apt-get install dos2unix - name: "Checkout" - if: steps.value_holder.outputs.CL_ENABLED + if: steps.value_holder.outputs.ACTIONS_ENABLED uses: actions/checkout@v1 with: fetch-depth: 25 - name: "Compile" - if: steps.value_holder.outputs.CL_ENABLED + if: steps.value_holder.outputs.ACTIONS_ENABLED run: | - python tools/changelog/ss13_genchangelog.py html/changelog.html html/changelogs - - name: "Convert Lineendings" - if: steps.value_holder.outputs.CL_ENABLED - run: | - unix2dos html/changelogs/.all_changelog.yml + python tools/ss13_genchangelog.py html/changelogs - name: Commit - if: steps.value_holder.outputs.CL_ENABLED + if: steps.value_holder.outputs.ACTIONS_ENABLED run: | git config --local user.email "action@github.com" git config --local user.name "Changelogs" git pull origin master + git add html/changelogs git commit -m "Automatic changelog compile [ci skip]" -a || true - name: "Push" - if: steps.value_holder.outputs.CL_ENABLED + if: steps.value_holder.outputs.ACTIONS_ENABLED uses: ad-m/github-push-action@master with: github_token: ${{ secrets.CHANGELOG_TOKEN }} diff --git a/.gitignore b/.gitignore index 6a3f09fd6245..6a5d235a316b 100644 --- a/.gitignore +++ b/.gitignore @@ -205,3 +205,4 @@ Temporary Items # tool-generated files check_regex_output.txt + diff --git a/check_regex.yaml b/check_regex.yaml index d79a1fd62a21..580694b882b5 100644 --- a/check_regex.yaml +++ b/check_regex.yaml @@ -38,7 +38,7 @@ standards: - exactly: [ - 292, + 291, "non-bitwise << uses", '(?, .. +GLOBAL_DATUM_INIT(filename_forbidden_chars, /regex, regex(@{""|[\\\n\t/?%*:|<>]|\.\."}, "g")) +GLOBAL_PROTECT(filename_forbidden_chars) +// had to use the OR operator for quotes instead of putting them in the character class because it breaks the syntax highlighting otherwise. diff --git a/code/datums/changelog.dm b/code/datums/changelog.dm new file mode 100644 index 000000000000..0b56cc5408ad --- /dev/null +++ b/code/datums/changelog.dm @@ -0,0 +1,52 @@ +GLOBAL_DATUM(changelog_tgui, /datum/changelog) + +/datum/changelog + var/static/list/changelog_items = list() + +/datum/changelog/ui_state() + return GLOB.always_state + +/datum/changelog/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "Changelog") + ui.open() + +/datum/changelog/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + if(action == "get_month") + var/datum/asset/changelog_item/changelog_item = changelog_items[params["date"]] + if (!changelog_item) + changelog_item = new /datum/asset/changelog_item(params["date"]) + changelog_items[params["date"]] = changelog_item + return ui.send_asset(changelog_item) + +/datum/changelog/ui_static_data() + var/list/data = list( "dates" = list() ) + var/regex/ymlRegex = regex(@"\.yml", "g") + + for(var/archive_file in sortList(flist("html/changelogs/archive/"))) + var/archive_date = ymlRegex.Replace(archive_file, "") + data["dates"] = list(archive_date) + data["dates"] + + return data + +/datum/asset/changelog_item + _abstract = /datum/asset/changelog_item + var/item_filename + +/datum/asset/changelog_item/New(date) + item_filename = SANITIZE_FILENAME("[date].yml") + SSassets.transport.register_asset(item_filename, file("html/changelogs/archive/" + item_filename)) + +/datum/asset/changelog_item/send(client) + if (!item_filename) + return + . = SSassets.transport.send_assets(client, item_filename) + +/datum/asset/changelog_item/get_url_mappings() + if (!item_filename) + return + . = list("[item_filename]" = SSassets.transport.get_asset_url(item_filename)) diff --git a/code/game/world.dm b/code/game/world.dm index c13da4f64d3e..1632ac04fba3 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -161,7 +161,8 @@ GLOBAL_VAR(restart_counter) start_log(GLOB.tgui_log) start_log(GLOB.world_shuttle_log) - GLOB.changelog_hash = md5('html/changelog.html') //for telling if the changelog has changed recently + var/latest_changelog = file("[global.config.directory]/../html/changelogs/archive/" + time2text(world.timeofday, "YYYY-MM") + ".yml") + GLOB.changelog_hash = fexists(latest_changelog) ? md5(latest_changelog) : 0 //for telling if the changelog has changed recently if(fexists(GLOB.config_error_log)) fcopy(GLOB.config_error_log, "[GLOB.log_directory]/config_error.log") fdel(GLOB.config_error_log) diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index aaf520690c08..d1950232b1bb 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -121,30 +121,6 @@ /datum/asset/simple/IRV ) -/datum/asset/simple/namespaced/changelog - assets = list( - "88x31.png" = 'html/88x31.png', - "bug-minus.png" = 'html/bug-minus.png', - "cross-circle.png" = 'html/cross-circle.png', - "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png', - "image-minus.png" = 'html/image-minus.png', - "image-plus.png" = 'html/image-plus.png', - "music-minus.png" = 'html/music-minus.png', - "music-plus.png" = 'html/music-plus.png', - "tick-circle.png" = 'html/tick-circle.png', - "wrench-screwdriver.png" = 'html/wrench-screwdriver.png', - "spell-check.png" = 'html/spell-check.png', - "burn-exclamation.png" = 'html/burn-exclamation.png', - "chevron.png" = 'html/chevron.png', - "chevron-expand.png" = 'html/chevron-expand.png', - "scales.png" = 'html/scales.png', - "coding.png" = 'html/coding.png', - "ban.png" = 'html/ban.png', - "chrome-wrench.png" = 'html/chrome-wrench.png', - "changelog.css" = 'html/changelog.css' - ) - parents = list("changelog.html" = 'html/changelog.html') - /datum/asset/simple/jquery legacy = TRUE assets = list( diff --git a/interface/interface.dm b/interface/interface.dm index 4735d537fb64..c753146f7972 100644 --- a/interface/interface.dm +++ b/interface/interface.dm @@ -93,9 +93,10 @@ /client/verb/changelog() set name = "Changelog" set category = "OOC" - var/datum/asset/simple/namespaced/changelog = get_asset_datum(/datum/asset/simple/namespaced/changelog) - changelog.send(src) - src << browse(changelog.get_htmlloader("changelog.html"), "window=changes;size=675x650") + if(!GLOB.changelog_tgui) + GLOB.changelog_tgui = new /datum/changelog() + + GLOB.changelog_tgui.ui_interact(mob) if(prefs.lastchangelog != GLOB.changelog_hash) prefs.lastchangelog = GLOB.changelog_hash prefs.save_preferences() diff --git a/shiptest.dme b/shiptest.dme index 82c95fafa004..f7141e203c9b 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -376,6 +376,7 @@ #include "code\datums\beam.dm" #include "code\datums\browser.dm" #include "code\datums\callback.dm" +#include "code\datums\changelog.dm" #include "code\datums\chatmessage.dm" #include "code\datums\cinematic.dm" #include "code\datums\dash_weapon.dm" diff --git a/tgui/packages/tgui/interfaces/Changelog.js b/tgui/packages/tgui/interfaces/Changelog.js new file mode 100644 index 000000000000..b3c4abb0f051 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Changelog.js @@ -0,0 +1,359 @@ +import { classes } from 'common/react'; +import { useBackend } from '../backend'; +import { Component, Fragment } from 'inferno'; +import { + Box, + Button, + Dropdown, + Icon, + Section, + Stack, + Table, +} from '../components'; +import { Window } from '../layouts'; +import { resolveAsset } from '../assets'; +import dateformat from 'dateformat'; +import yaml from 'js-yaml'; + +const icons = { + bugfix: { icon: 'bug', color: 'green' }, + wip: { icon: 'hammer', color: 'orange' }, + qol: { icon: 'hand-holding-heart', color: 'green' }, + soundadd: { icon: 'tg-sound-plus', color: 'green' }, + sounddel: { icon: 'tg-sound-minus', color: 'red' }, + add: { icon: 'check-circle', color: 'green' }, + expansion: { icon: 'check-circle', color: 'green' }, + rscadd: { icon: 'check-circle', color: 'green' }, + rscdel: { icon: 'times-circle', color: 'red' }, + imageadd: { icon: 'tg-image-plus', color: 'green' }, + imagedel: { icon: 'tg-image-minus', color: 'red' }, + spellcheck: { icon: 'spell-check', color: 'green' }, + experiment: { icon: 'radiation', color: 'yellow' }, + balance: { icon: 'balance-scale-right', color: 'yellow' }, + code_imp: { icon: 'code', color: 'green' }, + refactor: { icon: 'tools', color: 'green' }, + config: { icon: 'cogs', color: 'purple' }, + admin: { icon: 'user-shield', color: 'purple' }, + server: { icon: 'server', color: 'purple' }, + tgs: { icon: 'toolbox', color: 'purple' }, + tweak: { icon: 'wrench', color: 'green' }, + unknown: { icon: 'info-circle', color: 'label' }, +}; + +export class Changelog extends Component { + constructor() { + super(); + this.state = { + data: 'Loading changelog data...', + selectedDate: '', + selectedIndex: 0, + }; + this.dateChoices = []; + } + + setData(data) { + this.setState({ data }); + } + + setSelectedDate(selectedDate) { + this.setState({ selectedDate }); + } + + setSelectedIndex(selectedIndex) { + this.setState({ selectedIndex }); + } + + getData = (date, attemptNumber = 1) => { + const { act } = useBackend(this.context); + const self = this; + const maxAttempts = 6; + + if (attemptNumber > maxAttempts) { + return this.setData( + 'Failed to load data after ' + maxAttempts + ' attempts' + ); + } + + act('get_month', { date }); + + fetch(resolveAsset(date + '.yml')).then(async (changelogData) => { + const result = await changelogData.text(); + const errorRegex = /^Cannot find/; + + if (errorRegex.test(result)) { + const timeout = 50 + attemptNumber * 50; + + self.setData('Loading changelog data' + '.'.repeat(attemptNumber + 3)); + setTimeout(() => { + self.getData(date, attemptNumber + 1); + }, timeout); + } else { + self.setData(yaml.load(result, { schema: yaml.CORE_SCHEMA })); + } + }); + }; + + componentDidMount() { + const { + data: { dates = [] }, + } = useBackend(this.context); + + if (dates) { + dates.forEach((date) => + this.dateChoices.push(dateformat(date, 'mmmm yyyy', true)) + ); + this.setSelectedDate(this.dateChoices[0]); + this.getData(dates[0]); + } + } + + render() { + const { data, selectedDate, selectedIndex } = this.state; + const { + data: { dates }, + } = useBackend(this.context); + const { dateChoices } = this; + + const dateDropdown = dateChoices.length > 0 && ( + + +